chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:22:34 +08:00
commit 4b22cfda96
9037 changed files with 2363717 additions and 0 deletions
+32
View File
@@ -0,0 +1,32 @@
import React from 'react';
import APIModules from '../../api_modules.json';
import useBaseUrl from '@docusaurus/useBaseUrl';
import Link from '@docusaurus/Link';
const getModule = (fn: string) => {
const parts = fn.split('.');
// find the longest matching module
for (let i = parts.length; i > 0; i--) {
const module = parts.slice(0, i).join('.');
if (APIModules[module]) {
return module;
}
}
return null;
};
export function APILink({ fn, children, hash }: { fn: string; children?: React.ReactNode; hash?: string }) {
const module = getModule(fn);
if (!module) {
return <>{children}</>;
}
const docLink = useBaseUrl(`/${APIModules[module]}#${hash ?? fn}`);
return (
<a href={docLink} target="_blank">
{children ?? <code>{fn}()</code>}
</a>
);
}
@@ -0,0 +1,5 @@
import styles from './styles.module.css';
export default function BorderedContainer({ children }): JSX.Element {
return <section className={styles.Container}>{children}</section>;
}
@@ -0,0 +1,9 @@
.Container {
flex: 1;
padding: var(--padding-lg);
border: 2px solid var(--docusaurus-tag-list-border);
border-radius: 16px;
margin: 0 5px;
box-sizing: border-box;
text-align: left;
}
+315
View File
@@ -0,0 +1,315 @@
.CardGroup {
position: relative;
display: grid;
grid-gap: 20px;
margin-top: 20px;
}
.NoGap {
grid-gap: 0;
}
.MaxThreeColumns {
grid-template-columns: repeat(3, minmax(200px, 33%));
}
.AutofillColumns {
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
@media (max-width: 1500px) {
.MaxThreeColumns {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 750px) {
.MaxThreeColumns {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 650px) {
.MaxThreeColumns {
grid-template-columns: repeat(2, 1fr);
}
}
.Card {
display: flex;
flex: 1;
flex-direction: column;
align-items: center;
justify-content: center;
max-width: 100%;
min-width: 0;
}
.CardBordered {
padding: 24px;
border: 1px solid var(--docusaurus-tag-list-border);
border-radius: var(--card-border-radius);
transition: box-shadow 0.2s ease-in-out;
color: var(--ifm-heading-color);
}
.CardBordered:hover {
box-shadow: 0 0 11px rgba(33, 33, 33, 0.2);
box-shadow: var(--card-hover-shadow);
}
.CardBody {
display: flex;
flex-grow: 1;
}
.TextColor {
color: var(--ifm-font-color-base);
}
.BoxRoot {
box-sizing: border-box;
}
.FlexWrapNowrap {
flex-wrap: nowrap;
}
.FlexJustifyContentFlexStart {
justify-content: flex-start;
}
.FlexDirectionRow {
flex-direction: row;
}
.FlexAlignItemsCenter {
align-items: center;
}
.FlexFlex {
display: flex;
}
.Link {
text-decoration: none;
}
.Link:hover {
text-decoration: none;
}
.MarginLeft4 {
margin-left: var(--padding-xs);
}
.MarginTop4 {
margin-top: var(--padding-xs);
}
.PaddingBottom4 {
padding-bottom: var(--padding-xs);
}
.LogoCardContent {
display: flex;
flex-direction: column;
gap: var(--padding-md);
}
.LogoCardImage {
height: 80px;
display: flex;
justify-content: center;
align-self: stretch;
}
.LogoCardImage span {
font-size: 1.25rem;
align-items: center;
display: flex;
max-width: 80%;
}
.LogoCardImage span img {
max-height: 100%;
object-fit: contain;
}
.SmallLogoCardContent {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: stretch;
width: 100%;
}
.SmallLogoCardRounded {
border-radius: 12px;
}
.SmallLogoCardImage {
justify-content: center;
}
.SmallLogoCardImageWithTitle {
max-width: 30px;
max-height: 30px;
}
.SmallLogoCardImageDefault {
max-width: 200px;
max-height: 60px;
}
.SmallLogoCardRow {
display: flex;
align-items: center;
gap: 12px;
width: 100%;
}
.SmallLogoCardLabel {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
flex: 1;
font-weight: 600;
color: var(--ifm-heading-color);
font-size: 1.2rem;
text-align: left;
}
.SmallLogoCardArrow {
font-size: 1.1rem;
}
.SmallLogoCardLink {
color: var(--ifm-heading-color);
display: flex;
flex-direction: column;
align-items: stretch;
text-decoration: none;
}
.SmallLogoCardLink:hover {
color: var(--ifm-heading-color);
text-decoration: none;
}
.NewFeatureCardContent {
display: flex;
flex-direction: column;
align-items: center;
}
.NewFeatureCardHeading {
color: var(--ifm-color-primary-darkest);
font-size: 1.5rem;
line-height: 1;
text-align: center;
}
.NewFeatureCardHeadingSeparator {
display: inline-block;
width: 40%;
height: 2;
}
.NewFeatureCardTags {
align-self: stretch;
display: flex;
justify-content: space-between;
}
.NewFeatureCardWrapper {
display: flex;
flex-direction: column;
justify-content: space-between;
flex: 1;
}
.TitleCardContent {
width: 100%;
}
.TitleCardHeader {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.TitleCardHeaderRight {
display: flex;
align-items: center;
gap: 8px;
color: var(--ifm-font-color-base);
}
/* Ensure right header keeps neutral color even when the card link applies hover styles */
.Link .TitleCardHeaderRight,
.Link:hover .TitleCardHeaderRight {
color: var(--ifm-font-color-base);
}
.TitleCardTitle {
font-size: 1.2rem;
color: var(--ifm-heading-color);
font-weight: 600;
}
.TitleCardSeparator {
border: 0;
border-top: 1px solid var(--docusaurus-tag-list-border);
}
/* Column configurations */
.Cols1 {
grid-template-columns: 1fr;
}
.Cols2 {
grid-template-columns: repeat(2, 1fr);
}
.Cols3 {
grid-template-columns: repeat(3, 1fr);
}
.Cols4 {
grid-template-columns: repeat(4, 1fr);
}
.Cols5 {
grid-template-columns: repeat(5, 1fr);
}
.Cols6 {
grid-template-columns: repeat(6, 1fr);
}
/* Responsive behavior for column layouts */
@media (max-width: 1500px) {
.Cols6,
.Cols5,
.Cols4 {
grid-template-columns: repeat(3, 1fr);
}
}
@media (max-width: 1200px) {
.Cols6,
.Cols5,
.Cols4,
.Cols3 {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 750px) {
.Cols6,
.Cols5,
.Cols4,
.Cols3 {
grid-template-columns: repeat(2, 1fr);
}
.Cols2 {
grid-template-columns: 1fr;
}
}
+162
View File
@@ -0,0 +1,162 @@
import clsx from 'clsx';
import styles from './card.module.css';
import Link from '@docusaurus/Link';
export const CardGroup = ({ children, isSmall, cols, noGap }): JSX.Element => (
<div
className={clsx(
styles.CardGroup,
isSmall ? styles.AutofillColumns : cols ? styles[`Cols${cols}`] : styles.MaxThreeColumns,
noGap && styles.NoGap,
)}
>
{children}
</div>
);
export const Card = ({ children, link = '', style = undefined }): JSX.Element => {
if (!link) {
return (
<div className={clsx(styles.Card, styles.CardBordered)} style={style}>
{children}
</div>
);
}
return (
<Link className={clsx(styles.Link, styles.Card, styles.CardBordered)} style={style} to={link}>
{children}
</Link>
);
};
export const PageCard = ({ headerText, link, text }): JSX.Element => (
<Card link={link}>
<span>
<div className={clsx(styles.CardTitle, styles.BoxRoot, styles.PaddingBottom4)} style={{ pointerEvents: 'none' }}>
<div
className={clsx(
styles.BoxRoot,
styles.FlexFlex,
styles.FlexAlignItemsCenter,
styles.FlexDirectionRow,
styles.FlexJustifyContentFlexStart,
styles.FlexWrapNowrap,
)}
style={{ marginLeft: '-4px', marginTop: '-4px' }}
>
<div
className={clsx(styles.BoxRoot, styles.BoxHideIfEmpty, styles.MarginTop4, styles.MarginLeft4)}
style={{ pointerEvents: 'auto' }}
>
<span className="">{headerText}</span>
</div>
</div>
</div>
<span className={clsx(styles.TextColor, styles.CardBody)}>
<p>{text}</p>
</span>
</span>
</Card>
);
export const LogoCard = ({ description, children, link }): JSX.Element => (
<Card link={link}>
<div className={styles.LogoCardContent}>
<div className={styles.LogoCardImage}>{children}</div>
<p className={styles.TextColor}>{description}</p>
</div>
</Card>
);
export const SmallLogoCard = ({ children, link, title = '' }) => {
return (
<Link
className={clsx(styles.Card, styles.CardBordered, styles.SmallLogoCardRounded, styles.SmallLogoCardLink)}
to={link}
>
<div className={styles.SmallLogoCardContent}>
{title ? (
<div className={styles.SmallLogoCardRow}>
<div
className={clsx(
'max-height-img-container',
styles.SmallLogoCardImage,
styles.SmallLogoCardImageWithTitle,
)}
>
{children}
</div>
<div className={styles.SmallLogoCardLabel}>
<span>{title}</span>
</div>
</div>
) : (
<div
className={clsx('max-height-img-container', styles.SmallLogoCardImage, styles.SmallLogoCardImageDefault)}
>
{children}
</div>
)}
</div>
</Link>
);
};
const RELEASE_URL = 'https://github.com/mlflow/mlflow/releases/tag/v';
export const NewFeatureCard = ({ children, description, name, releaseVersion, learnMoreLink = '' }) => (
<Card>
<div className={styles.NewFeatureCardWrapper}>
<div className={styles.NewFeatureCardContent}>
<div className={styles.NewFeatureCardHeading}>
{name}
<br />
<hr className={styles.NewFeatureCardHeadingSeparator} />
</div>
<div className={styles.LogoCardImage}>{children}</div>
<br />
<p>{description}</p>
<br />
</div>
<div className={styles.NewFeatureCardTags}>
<div>
{learnMoreLink && (
<Link className="button button--outline button--sm button--primary" to={learnMoreLink}>
Learn more
</Link>
)}
</div>
<Link className="button button--outline button--sm button--primary" to={`${RELEASE_URL}${releaseVersion}`}>
released in {releaseVersion}
</Link>
</div>
</div>
</Card>
);
export const TitleCard = ({
title,
description,
link = '',
headerRight = undefined,
children = undefined,
}): JSX.Element => (
<Card link={link}>
<div className={styles.TitleCardContent}>
<div className={clsx(styles.TitleCardHeader)}>
<div className={clsx(styles.TitleCardTitle)} style={{ textAlign: 'left', fontWeight: 'bold' }}>
{title}
</div>
<div className={styles.TitleCardHeaderRight}>{headerRight}</div>
</div>
<hr className={clsx(styles.TitleCardSeparator)} style={{ margin: '12px 0' }} />
{children ? (
<div className={clsx(styles.TextColor)}>{children}</div>
) : (
<p className={clsx(styles.TextColor)} dangerouslySetInnerHTML={{ __html: description }} />
)}
</div>
</Card>
);
@@ -0,0 +1,27 @@
import React, { useState } from 'react';
import { ChevronDown, ChevronUp } from 'lucide-react';
import styles from './styles.module.css';
interface CollapsibleSectionProps {
children: React.ReactNode;
title: string;
defaultExpanded?: boolean;
}
const CollapsibleSection: React.FC<CollapsibleSectionProps> = ({ children, title, defaultExpanded = false }) => {
const [isExpanded, setIsExpanded] = useState(defaultExpanded);
return (
<div className={styles.collapsibleContainer}>
<div className={styles.header}>
<button className={styles.toggleButton} onClick={() => setIsExpanded(!isExpanded)} aria-expanded={isExpanded}>
<span className={styles.toggleText}>{title}</span>
{isExpanded ? <ChevronUp size={20} /> : <ChevronDown size={20} />}
</button>
</div>
{isExpanded && <div className={styles.content}>{children}</div>}
</div>
);
};
export default CollapsibleSection;
@@ -0,0 +1,60 @@
.collapsibleContainer {
margin: 2rem 0;
}
.header {
margin-bottom: 1rem;
max-width: 900px;
margin-left: auto;
margin-right: auto;
display: flex;
justify-content: center;
align-items: center;
}
.toggleButton {
display: flex;
align-items: center;
justify-content: space-between;
width: 85%;
padding: 1rem 1.5rem;
background: var(--ifm-background-surface-color);
border: 2px solid var(--ifm-color-emphasis-300);
border-radius: 8px;
cursor: pointer;
transition: all 0.2s ease;
font-size: 1rem;
font-weight: 500;
color: var(--ifm-color-emphasis-800);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}
.toggleButton:hover {
background: var(--ifm-color-emphasis-100);
border-color: #0194e2;
color: #0194e2;
}
.toggleButton:focus {
outline: 2px solid #0194e2;
outline-offset: 2px;
}
.toggleText {
flex: 1;
text-align: left;
}
.content {
max-width: 900px;
margin: 0 auto;
}
/* Responsive design */
@media (max-width: 768px) {
.toggleButton {
padding: 0.75rem 1rem;
font-size: 0.9rem;
width: 85%;
}
}
@@ -0,0 +1,37 @@
import React from 'react';
import { LucideIcon } from 'lucide-react';
import styles from './styles.module.css';
export interface ConceptItem {
icon?: LucideIcon;
title: string;
description: string;
}
interface ConceptOverviewProps {
concepts: ConceptItem[];
title?: string;
}
export default function ConceptOverview({ concepts, title }: ConceptOverviewProps) {
return (
<div className={styles.conceptOverview}>
{title && <h3 className={styles.overviewTitle}>{title}</h3>}
<div className={styles.conceptGrid}>
{concepts.map((concept, index) => (
<div key={index} className={styles.conceptCard}>
<div className={styles.conceptHeader}>
{concept.icon && (
<div className={styles.conceptIcon}>
<concept.icon size={20} />
</div>
)}
<h4 className={styles.conceptTitle}>{concept.title}</h4>
</div>
<p className={styles.conceptDescription}>{concept.description}</p>
</div>
))}
</div>
</div>
);
}
@@ -0,0 +1,102 @@
/* Define color variables matching TileCard blues */
:root {
--concept-primary: #0194e2;
--concept-primary-dark: #0066cc;
--concept-primary-light: rgba(1, 148, 226, 0.3);
--concept-primary-lightest: rgba(1, 148, 226, 0.1);
--concept-border: var(--ifm-border-color);
--concept-text-primary: var(--ifm-color-emphasis-900);
--concept-text-secondary: var(--ifm-color-emphasis-700);
}
:global([data-theme='dark']) {
--concept-primary: #0194e2;
--concept-primary-dark: #5cb7cc;
--concept-primary-light: rgba(1, 148, 226, 0.3);
--concept-primary-lightest: rgba(1, 148, 226, 0.15);
--concept-border: var(--ifm-border-color);
--concept-text-primary: var(--ifm-color-emphasis-900);
--concept-text-secondary: var(--ifm-color-emphasis-700);
}
.conceptOverview {
margin: 2rem 0;
}
.overviewTitle {
font-size: 1.25rem;
margin-bottom: 1.5rem;
color: var(--concept-text-primary);
text-align: center;
font-weight: 600;
}
.conceptGrid {
display: grid;
grid-template-columns: 1fr;
margin: 0 1rem;
}
.conceptCard {
border-radius: 8px;
padding: 1.5rem;
background: transparent;
}
.conceptHeader {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 0.75rem;
}
.conceptIcon {
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
border-radius: 6px;
background: var(--concept-primary-lightest);
color: var(--concept-primary);
flex-shrink: 0;
}
.conceptTitle {
font-size: 1.1rem;
font-weight: 600;
margin: 0;
color: var(--concept-text-primary);
}
.conceptDescription {
color: var(--concept-text-secondary);
font-size: 0.95rem;
line-height: 1.6;
margin: 0;
}
/* Dark mode styles */
[data-theme='dark'] .conceptCard {
background: transparent;
box-shadow: none;
}
/* Responsive design */
@media (min-width: 768px) {
.conceptGrid {
grid-template-columns: repeat(2, 1fr);
gap: 1.5rem;
}
}
@media (min-width: 1024px) {
.conceptGrid {
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}
.conceptCard {
padding: 2rem;
}
}
+287
View File
@@ -0,0 +1,287 @@
import React, { useState, useRef } from 'react';
import { LucideIcon } from 'lucide-react';
import styles from './styles.module.css';
interface DAGStep {
title: string;
description: string;
detailedDescription?: string;
icon?: LucideIcon;
highlight?: boolean;
isFocus?: boolean;
}
interface DAGLoopProps {
steps: DAGStep[];
title?: string;
loopBackIcon?: LucideIcon;
loopBackText?: string;
loopBackDescription?: string;
circleSize?: number;
}
const DAGLoop: React.FC<DAGLoopProps> = ({
steps,
title,
loopBackIcon: LoopIcon,
loopBackText,
loopBackDescription,
circleSize = 400,
}) => {
const [hoveredStep, setHoveredStep] = useState<number | null>(null);
const [hoveredCenter, setHoveredCenter] = useState(false);
const [tooltipPosition, setTooltipPosition] = useState({ x: 0, y: 0 });
const [isMobile, setIsMobile] = useState(false);
const containerRef = useRef<HTMLDivElement>(null);
// Detect mobile viewport
React.useEffect(() => {
const checkMobile = () => {
setIsMobile(window.innerWidth <= 768);
};
checkMobile();
window.addEventListener('resize', checkMobile);
return () => window.removeEventListener('resize', checkMobile);
}, []);
// Dynamic scaling based on number of elements
const getOptimalRadius = () => {
const baseRadius = circleSize / 2;
const minRadius = isMobile ? 100 : 140;
const maxRadius = isMobile ? 130 : 220;
// Adjust radius based on number of steps
// More steps = larger radius for better spacing
const scaleFactor = Math.min(1.2, 0.8 + steps.length * 0.05);
const calculatedRadius = (baseRadius - (isMobile ? 50 : 80)) * scaleFactor;
return Math.max(minRadius, Math.min(maxRadius, calculatedRadius));
};
// Adjust size for mobile and dynamic scaling
const actualCircleSize = isMobile ? 280 : circleSize;
const radius = getOptimalRadius();
const centerX = actualCircleSize / 2;
const centerY = actualCircleSize / 2;
const calculatePosition = (index: number) => {
const angle = (index * 2 * Math.PI) / steps.length - Math.PI / 2;
const x = centerX + radius * Math.cos(angle);
const y = centerY + radius * Math.sin(angle);
return { x, y };
};
const calculateArrowPath = (fromIndex: number, toIndex: number) => {
// Get positions of both nodes
const fromPos = calculatePosition(fromIndex);
const toPos = calculatePosition(toIndex);
// Calculate vector from center to center
const dx = toPos.x - fromPos.x;
const dy = toPos.y - fromPos.y;
// Calculate the midpoint between the two nodes
const midX = (fromPos.x + toPos.x) / 2;
const midY = (fromPos.y + toPos.y) / 2;
// Normalize the direction vector
const length = Math.sqrt(dx * dx + dy * dy);
const dirX = dx / length;
const dirY = dy / length;
// Short arrow positioned at midpoint
const arrowLength = 20; // Total length of arrow line
// Start point of arrow (back from midpoint along the direction vector)
const startX = midX - (arrowLength / 2) * dirX;
const startY = midY - (arrowLength / 2) * dirY;
// End point of arrow (forward from midpoint along the direction vector)
const endX = midX + (arrowLength / 2) * dirX;
const endY = midY + (arrowLength / 2) * dirY;
// Simple straight line
return `M ${startX} ${startY} L ${endX} ${endY}`;
};
const handleMouseEnter = (index: number, event: React.MouseEvent) => {
setHoveredStep(index);
if (containerRef.current) {
const rect = containerRef.current.getBoundingClientRect();
const position = calculatePosition(index);
setTooltipPosition({
x: position.x,
y: position.y,
});
}
};
const handleMouseLeave = () => {
setHoveredStep(null);
};
// Render linear layout for mobile
if (isMobile) {
return (
<div className={styles.loopContainer}>
{title && <h3 className={styles.loopTitle}>{title}</h3>}
<div className={styles.mobileLinearContent}>
{steps.map((step, index) => (
<div key={index} className={styles.mobileStepItem}>
<div className={styles.mobileStepIndicator}>
<div className={`${styles.mobileStepNumber} ${step.isFocus ? styles.mobileFocusNode : ''}`}>
{step.icon ? <step.icon size={20} /> : <span>{index + 1}</span>}
</div>
{index < steps.length - 1 && <div className={styles.mobileStepConnector} />}
</div>
<div className={styles.mobileStepContent}>
<h4 className={styles.mobileStepTitle}>{step.title}</h4>
<p className={styles.mobileStepDescription}>{step.detailedDescription || step.description}</p>
</div>
</div>
))}
{LoopIcon && loopBackDescription && (
<div className={styles.mobileLoopBack}>
<div className={styles.mobileLoopIcon}>
<LoopIcon size={24} />
</div>
<div className={styles.mobileLoopContent}>
<h4 className={styles.mobileLoopTitle}>{loopBackText || 'Iterate'}</h4>
<p className={styles.mobileLoopDescription}>{loopBackDescription}</p>
</div>
</div>
)}
</div>
</div>
);
}
// Desktop circular layout
return (
<div className={styles.loopContainer}>
{title && <h3 className={styles.loopTitle}>{title}</h3>}
<div className={styles.loopContent}>
<div
className={styles.circleContainer}
ref={containerRef}
style={{
width: `${actualCircleSize}px`,
height: `${actualCircleSize}px`,
}}
>
<svg width={actualCircleSize} height={actualCircleSize} className={styles.svgCanvas}>
{/* Draw arrows between steps */}
{steps.map((_, index) => {
const nextIndex = (index + 1) % steps.length;
return (
<g key={`arrow-${index}`}>
<defs>
<marker id={`arrowhead-${index}`} markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto">
<path
d="M 0 0 L 6 3 L 0 6 L 1.5 3 Z"
fill="currentColor"
opacity="1"
className={styles.arrowHead}
/>
</marker>
</defs>
<path
d={calculateArrowPath(index, nextIndex)}
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeDasharray="0"
opacity="0.9"
markerEnd={`url(#arrowhead-${index})`}
className={styles.arrowPath}
/>
</g>
);
})}
{/* Draw loop indicator in center */}
{LoopIcon && (
<g
className={styles.centerIcon}
onMouseEnter={() => setHoveredCenter(true)}
onMouseLeave={() => setHoveredCenter(false)}
style={{ cursor: 'pointer' }}
>
<foreignObject x={centerX - 35} y={centerY - 35} width="70" height="70">
<div className={styles.loopIconWrapper}>
<LoopIcon size={32} />
</div>
</foreignObject>
{loopBackText && (
<text x={centerX} y={centerY + 50} textAnchor="middle" className={styles.loopText}>
{loopBackText}
</text>
)}
</g>
)}
</svg>
{/* Render step nodes */}
{steps.map((step, index) => {
const position = calculatePosition(index);
return (
<div
key={index}
className={`${styles.stepNode} ${step.highlight ? styles.highlighted : ''} ${step.isFocus ? styles.focusNode : ''}`}
style={{
left: `${position.x}px`,
top: `${position.y}px`,
transform: 'translate(-50%, -50%)',
}}
onMouseEnter={(e) => handleMouseEnter(index, e)}
onMouseLeave={handleMouseLeave}
>
<div className={styles.stepNodeContent}>
{step.icon ? <step.icon size={24} /> : <span className={styles.stepNumber}>{index + 1}</span>}
</div>
<div className={styles.stepLabel}>{step.title}</div>
</div>
);
})}
{/* Tooltip for steps */}
{hoveredStep !== null && (
<div
className={styles.tooltip}
style={{
left: `${tooltipPosition.x}px`,
top: `${tooltipPosition.y}px`,
transform: 'translate(-50%, -120%)',
}}
>
<h4 className={styles.tooltipTitle}>{steps[hoveredStep].title}</h4>
<p className={styles.tooltipDescription}>
{steps[hoveredStep].detailedDescription || steps[hoveredStep].description}
</p>
<div className={styles.tooltipArrow} />
</div>
)}
{/* Tooltip for center icon */}
{hoveredCenter && loopBackDescription && (
<div
className={styles.centerTooltip}
style={{
left: `${centerX}px`,
top: `${centerY}px`,
transform: 'translate(-50%, -50%)',
}}
>
<p className={styles.centerTooltipDescription}>{loopBackDescription}</p>
</div>
)}
</div>
</div>
</div>
);
};
export default DAGLoop;
@@ -0,0 +1,437 @@
/* Define color variables for light/dark mode - matching TileCard blues */
:root {
--dag-loop-primary: #0194e2;
--dag-loop-primary-dark: #0066cc;
--dag-loop-primary-light: rgba(1, 148, 226, 0.3);
--dag-loop-primary-lightest: rgba(1, 148, 226, 0.1);
--dag-loop-background: var(--ifm-background-color);
--dag-loop-border: var(--ifm-border-color);
--dag-loop-text-primary: var(--ifm-color-emphasis-900);
--dag-loop-text-secondary: var(--ifm-color-emphasis-700);
}
:global([data-theme='dark']) {
--dag-loop-primary: #0194e2;
--dag-loop-primary-dark: #5cb7cc;
--dag-loop-primary-light: rgba(1, 148, 226, 0.3);
--dag-loop-primary-lightest: rgba(1, 148, 226, 0.15);
--dag-loop-background: var(--ifm-background-color);
--dag-loop-border: var(--ifm-border-color);
--dag-loop-text-primary: var(--ifm-color-emphasis-900);
--dag-loop-text-secondary: var(--ifm-color-emphasis-700);
}
.loopContainer {
margin: 2rem 0;
position: relative;
overflow: visible;
min-height: 550px;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
max-width: 100%;
}
.loopTitle {
font-size: 1.25rem;
margin-bottom: 1rem;
color: var(--dag-loop-text-primary);
text-align: center;
font-weight: 600;
width: 100%;
}
.loopContent {
max-width: 900px;
margin: 0 auto;
padding: 1rem 1rem 2rem;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
}
/* Circle layout container */
.circleContainer {
position: relative;
margin: 1rem auto 2rem;
overflow: visible;
display: flex;
align-items: center;
justify-content: center;
}
.svgCanvas {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 1;
}
.arrowPath {
transition: opacity 0.2s ease;
color: var(--dag-loop-primary-light);
}
.arrowPath:hover {
opacity: 0.9 !important;
}
.arrowHead {
color: var(--dag-loop-primary-light);
}
/* Step nodes */
.stepNode {
position: absolute;
display: flex;
flex-direction: column;
align-items: center;
cursor: pointer;
transition: transform 0.3s ease;
z-index: 10;
}
.stepNode:hover {
transform: translate(-50%, -50%) scale(1.1);
}
.stepNodeContent {
width: 80px;
height: 80px;
border-radius: 50%;
background: var(--dag-loop-background);
border: 2px solid var(--dag-loop-border);
display: flex;
align-items: center;
justify-content: center;
color: var(--dag-loop-primary);
font-weight: 600;
font-size: 1.3rem;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
}
.stepNode:hover .stepNodeContent {
border-color: var(--dag-loop-primary);
background: var(--dag-loop-primary-lightest);
box-shadow: 0 4px 16px rgba(1, 148, 226, 0.2);
}
.stepNode.highlighted .stepNodeContent {
background: var(--dag-loop-primary-lightest);
border-color: var(--dag-loop-primary);
border-width: 3px;
}
/* Focus node styling - subtle emphasis */
.stepNode.focusNode .stepNodeContent {
background: var(--dag-loop-primary);
color: white;
border-color: var(--dag-loop-primary-dark);
border-width: 2px;
box-shadow: 0 4px 12px rgba(1, 148, 226, 0.25);
}
.stepNode.focusNode:hover .stepNodeContent {
background: var(--dag-loop-primary-dark);
box-shadow: 0 6px 16px rgba(1, 148, 226, 0.35);
}
.stepNumber {
font-size: 1.2rem;
font-weight: 600;
color: var(--dag-loop-primary);
}
.stepLabel {
margin-top: 0.75rem;
font-size: 0.9rem;
font-weight: 500;
color: var(--dag-loop-text-secondary);
text-align: center;
max-width: 120px;
line-height: 1.3;
}
/* Center loop icon */
.centerIcon {
opacity: 1;
}
.loopIconWrapper {
display: flex;
align-items: center;
justify-content: center;
width: 70px;
height: 70px;
border-radius: 50%;
background: var(--dag-loop-background);
border: 2px solid var(--dag-loop-primary-light);
color: var(--dag-loop-primary);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
}
.centerIcon:hover .loopIconWrapper {
border-color: var(--dag-loop-primary);
background: var(--dag-loop-primary-lightest);
box-shadow: 0 4px 16px rgba(1, 148, 226, 0.2);
}
.loopText {
fill: var(--dag-loop-primary);
font-size: 0.95rem;
font-weight: 600;
}
/* Tooltip */
.tooltip {
position: absolute;
background: var(--ifm-background-surface-color);
border: 2px solid var(--dag-loop-primary-light);
border-radius: 12px;
padding: 1rem 1.25rem;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
z-index: 1000;
min-width: 250px;
max-width: 350px;
pointer-events: none;
animation: fadeIn 0.2s ease;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translate(-50%, -110%);
}
to {
opacity: 1;
transform: translate(-50%, -120%);
}
}
.tooltipTitle {
font-size: 1.1rem;
font-weight: 600;
margin: 0 0 0.5rem 0;
color: var(--dag-loop-text-primary);
}
.tooltipDescription {
font-size: 0.9rem;
line-height: 1.5;
color: var(--dag-loop-text-secondary);
margin: 0;
}
.tooltipArrow {
position: absolute;
bottom: -8px;
left: 50%;
transform: translateX(-50%) rotate(45deg);
width: 16px;
height: 16px;
background: var(--ifm-background-surface-color);
border-right: 2px solid var(--dag-loop-primary-light);
border-bottom: 2px solid var(--dag-loop-primary-light);
}
/* Center tooltip */
.centerTooltip {
position: absolute;
background: var(--ifm-background-surface-color);
border: 2px solid var(--dag-loop-primary-light);
border-radius: 12px;
padding: 1.25rem 1.5rem;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
z-index: 1000;
max-width: 400px;
min-width: 300px;
pointer-events: none;
animation: fadeIn 0.2s ease;
margin-top: 140px;
}
.centerTooltipDescription {
color: var(--dag-loop-text-secondary);
font-size: 0.95rem;
line-height: 1.6;
margin: 0;
text-align: center;
}
/* Mobile linear layout styles */
.mobileLinearContent {
max-width: 100%;
padding: 1rem;
}
.mobileStepItem {
display: flex;
gap: 1rem;
margin-bottom: 0;
}
.mobileStepIndicator {
display: flex;
flex-direction: column;
align-items: center;
flex-shrink: 0;
}
.mobileStepNumber {
width: 40px;
height: 40px;
border-radius: 50%;
background: var(--dag-loop-primary-lightest);
border: 2px solid var(--dag-loop-primary);
display: flex;
align-items: center;
justify-content: center;
color: var(--dag-loop-primary);
font-weight: 600;
font-size: 0.9rem;
}
.mobileStepNumber.mobileFocusNode {
background: var(--dag-loop-primary);
color: white;
border-width: 2px;
box-shadow: 0 3px 8px rgba(1, 148, 226, 0.2);
}
.mobileStepConnector {
width: 2px;
height: 40px;
background: var(--dag-loop-primary-light);
margin: 4px 0;
}
.mobileStepContent {
flex: 1;
padding-bottom: 1.5rem;
}
.mobileStepTitle {
font-size: 1rem;
font-weight: 600;
margin: 0 0 0.5rem 0;
color: var(--dag-loop-text-primary);
}
.mobileStepDescription {
font-size: 0.9rem;
line-height: 1.5;
color: var(--dag-loop-text-secondary);
margin: 0;
}
.mobileLoopBack {
display: flex;
gap: 1rem;
padding: 1rem;
background: var(--dag-loop-background);
border: 2px solid var(--dag-loop-primary-light);
border-radius: 8px;
margin-top: 0.5rem;
}
.mobileLoopIcon {
width: 40px;
height: 40px;
border-radius: 50%;
background: var(--dag-loop-primary-lightest);
display: flex;
align-items: center;
justify-content: center;
color: var(--dag-loop-primary);
flex-shrink: 0;
}
.mobileLoopContent {
flex: 1;
}
.mobileLoopTitle {
font-size: 1rem;
font-weight: 600;
margin: 0 0 0.25rem 0;
color: var(--dag-loop-primary);
}
.mobileLoopDescription {
font-size: 0.9rem;
line-height: 1.5;
color: var(--dag-loop-text-secondary);
margin: 0;
}
/* Mobile responsive design */
@media (max-width: 768px) {
.loopContainer {
min-height: auto;
margin: 1.5rem 0;
padding: 0;
}
.loopContent {
padding: 1rem 0.5rem;
width: 100%;
max-width: 100%;
}
.circleContainer {
width: 280px;
height: 280px;
margin: 1rem auto;
transform: scale(0.9);
}
.stepNodeContent {
width: 44px;
height: 44px;
font-size: 1rem;
}
.stepLabel {
font-size: 0.7rem;
max-width: 70px;
}
.tooltip {
min-width: 200px;
max-width: 280px;
padding: 0.75rem 1rem;
}
.tooltipTitle {
font-size: 1rem;
}
.tooltipDescription {
font-size: 0.85rem;
}
.centerTooltip {
max-width: 280px;
min-width: 200px;
padding: 1rem;
margin-top: 100px;
}
.centerTooltipDescription {
font-size: 0.9rem;
}
.loopIconWrapper {
width: 50px;
height: 50px;
}
.loopText {
font-size: 0.75rem;
}
}
@@ -0,0 +1,34 @@
import React from 'react';
import { LucideIcon } from 'lucide-react';
import styles from './styles.module.css';
export interface FeatureItem {
icon?: LucideIcon;
title: string;
description: string;
}
interface FeatureHighlightsProps {
features: FeatureItem[];
col?: number;
}
export default function FeatureHighlights({ features, col = 2 }: FeatureHighlightsProps) {
return (
<div className={styles.featureHighlights} style={{ gridTemplateColumns: `repeat(${col}, 1fr)` }}>
{features.map((feature, index) => (
<div key={index} className={styles.highlightItem}>
{feature.icon && (
<div className={styles.highlightIcon}>
<feature.icon size={24} />
</div>
)}
<div className={styles.highlightContent}>
<h4>{feature.title}</h4>
<p>{feature.description}</p>
</div>
</div>
))}
</div>
);
}
@@ -0,0 +1,34 @@
.featureHighlights {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1.5rem;
margin-bottom: 2rem;
}
.highlightItem {
display: flex;
align-items: flex-start;
gap: 1rem;
padding: 1rem;
text-align: left;
}
.highlightIcon {
font-size: 1.5rem;
flex-shrink: 0;
margin-top: 0.25rem;
}
.highlightContent h4 {
font-size: 1.1rem;
margin-bottom: 0.5rem;
color: var(--ifm-color-emphasis-hover);
margin-top: 0;
}
.highlightContent p {
color: var(--ifm-border-color);
font-size: 0.9rem;
line-height: 1.5;
margin-bottom: 0;
}
+69
View File
@@ -0,0 +1,69 @@
import { cva } from 'class-variance-authority';
import { useColorMode } from '@docusaurus/theme-common';
import LogoDark from '@site/static/images/mlflow-logo-white.svg';
import LogoLight from '@site/static/images/mlflow-logo-black.svg';
import useBaseUrl from '@docusaurus/useBaseUrl';
import { FooterMenuItem } from '../FooterMenuItem/FooterMenuItem';
import { GradientWrapper, type Variant } from '../GradientWrapper/GradientWrapper';
const footerVariants = cva(
'pb-30 flex flex-col pt-30 bg-bottom bg-no-repeat bg-cover bg-size-[auto_360px] 2xl:bg-size-[100%_360px]',
{
variants: {
theme: {
dark: 'bg-brand-black',
light: 'bg-white',
},
},
},
);
const getLogo = (isDarkMode: boolean) => {
return isDarkMode ? LogoDark : LogoLight;
};
export const Footer = ({ variant }: { variant: Variant }) => {
const { colorMode } = useColorMode();
const isDarkMode = colorMode === 'dark';
const Logo = getLogo(isDarkMode);
return (
<GradientWrapper
variant={variant}
isFooter
className={footerVariants({ theme: colorMode })}
element="footer"
height={360}
>
<div className="flex flex-row justify-between items-start md:items-center px-6 lg:px-20 gap-10 xs:gap-0 max-w-container">
<div className="flex flex-col gap-8">
<Logo className="h-[36px] shrink-0" />
<div className="text-xs text-left md:text-nowrap md:w-0">
© 2025 MLflow Project, a Series of LF Projects, LLC.
</div>
</div>
<div className="flex flex-col flex-wrap justify-end md:text-right md:flex-row gap-x-10 lg:gap-x-20 gap-y-5 w-2/5 md:w-auto md:pt-2 max-w-fit">
{/* these routes are on the main mlflow.org site, which is hosted in a different repo */}
<FooterMenuItem href="https://mlflow.org" isDarkMode={isDarkMode}>
Components
</FooterMenuItem>
<FooterMenuItem href="https://mlflow.org/releases" isDarkMode={isDarkMode}>
Releases
</FooterMenuItem>
<FooterMenuItem href="https://mlflow.org/blog" isDarkMode={isDarkMode}>
Blog
</FooterMenuItem>
<FooterMenuItem href={useBaseUrl('/')} isDarkMode={isDarkMode}>
Docs
</FooterMenuItem>
<FooterMenuItem href="https://mlflow.org/ambassadors" isDarkMode={isDarkMode}>
Ambassador Program
</FooterMenuItem>
</div>
</div>
</GradientWrapper>
);
};
@@ -0,0 +1,27 @@
import Link from '@docusaurus/Link';
import { ComponentProps, ReactNode } from 'react';
import { cx } from 'class-variance-authority';
interface FooterMenuItemProps extends ComponentProps<typeof Link> {
isDarkMode: boolean;
children: ReactNode;
className?: string;
href: string;
}
export const FooterMenuItem = ({ className, isDarkMode, children, ...props }: FooterMenuItemProps) => {
return (
<div>
<Link
{...props}
className={cx(
'text-[15px] font-medium no-underline hover:no-underline transition-opacity hover:opacity-80',
isDarkMode ? 'text-white visited:text-white' : 'text-black visited:text-black',
className,
)}
>
{children}
</Link>
</div>
);
};
+240
View File
@@ -0,0 +1,240 @@
import React, { useEffect, useState, useRef } from 'react';
import { Search, ArrowUpDown, Plus, MessageSquare } from 'lucide-react';
import styles from './styles.module.css';
interface GitHubIssue {
number: number;
title: string;
html_url: string;
user: {
login: string;
avatar_url: string;
};
created_at: string;
comments: number;
reactions: {
'+1': number;
};
labels: Array<{
name: string;
color: string;
}>;
}
interface GitHubIssuesProps {
repo: string;
label?: string;
maxIssues?: number;
}
export default function GitHubIssues({
repo = 'mlflow/mlflow',
label = 'domain/genai',
maxIssues = 10,
}: GitHubIssuesProps): JSX.Element {
const [issues, setIssues] = useState<GitHubIssue[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [searchQuery, setSearchQuery] = useState('');
const [sortBy, setSortBy] = useState<'reactions' | 'created'>('reactions');
const [showSortMenu, setShowSortMenu] = useState(false);
const sortMenuRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const fetchIssues = async () => {
setLoading(true);
setError(null);
try {
// Build query with optional search
let query = `repo:${repo} is:issue state:open label:${label}`;
if (searchQuery.trim()) {
query += ` ${searchQuery.trim()}`;
}
const sortParam = sortBy === 'reactions' ? 'reactions' : 'created';
const url = `https://api.github.com/search/issues?q=${encodeURIComponent(query)}&sort=${sortParam}&order=desc&per_page=${maxIssues}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error('Failed to fetch issues');
}
const data = await response.json();
setIssues(data.items || []);
} catch (err) {
setError(err instanceof Error ? err.message : 'An error occurred');
} finally {
setLoading(false);
}
};
// Debounce search
const timeoutId = setTimeout(() => {
fetchIssues();
}, 300);
return () => clearTimeout(timeoutId);
}, [repo, label, maxIssues, searchQuery, sortBy]);
// Close sort menu when clicking outside
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (sortMenuRef.current && !sortMenuRef.current.contains(event.target as Node)) {
setShowSortMenu(false);
}
};
if (showSortMenu) {
document.addEventListener('mousedown', handleClickOutside);
}
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [showSortMenu]);
const formatDate = (dateString: string): string => {
const date = new Date(dateString);
return date.toLocaleDateString('en-US', {
month: 'numeric',
day: 'numeric',
year: 'numeric',
});
};
if (loading) {
return (
<div className={styles.container}>
<div className={styles.header}>
<h3 className={styles.title}>Feature requests</h3>
</div>
<div className={styles.loading}>Loading feature requests...</div>
</div>
);
}
if (error) {
return (
<div className={styles.container}>
<div className={styles.header}>
<h3 className={styles.title}>Feature requests</h3>
</div>
<div className={styles.error}>
<p>Unable to load issues. Please visit GitHub directly:</p>
<a
href={`https://github.com/${repo}/issues?q=is%3Aissue+state%3Aopen+label%3A${label}+sort%3Areactions-%2B1-desc`}
target="_blank"
rel="noopener noreferrer"
className={styles.errorLink}
>
View on GitHub
</a>
</div>
</div>
);
}
return (
<div className={styles.container}>
<div className={styles.header}>
<h3 className={styles.title}>Feature requests</h3>
<div className={styles.headerActions}>
<div className={styles.searchBox}>
<Search size={16} className={styles.searchIcon} />
<input
type="text"
placeholder="Search..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className={styles.searchInput}
/>
</div>
<div className={styles.sortDropdown} ref={sortMenuRef}>
<button className={styles.sortButton} onClick={() => setShowSortMenu(!showSortMenu)}>
<ArrowUpDown size={16} />
{sortBy === 'reactions' ? 'Upvotes' : 'Recent'}
</button>
{showSortMenu && (
<div className={styles.sortMenu}>
<button
className={sortBy === 'reactions' ? styles.sortMenuItemActive : styles.sortMenuItem}
onClick={() => {
setSortBy('reactions');
setShowSortMenu(false);
}}
>
Upvotes
</button>
<button
className={sortBy === 'created' ? styles.sortMenuItemActive : styles.sortMenuItem}
onClick={() => {
setSortBy('created');
setShowSortMenu(false);
}}
>
Recent
</button>
</div>
)}
</div>
<a
href={`https://github.com/${repo}/issues/new?template=feature_request_template.yaml`}
target="_blank"
rel="noopener noreferrer"
className={styles.newButton}
>
<Plus size={16} />
New
</a>
</div>
</div>
<div className={styles.issuesList}>
{issues.map((issue) => (
<a
key={issue.number}
href={issue.html_url}
target="_blank"
rel="noopener noreferrer"
className={styles.issueRow}
>
<div className={styles.voteColumn}>
<div className={styles.voteCount}>{issue.reactions['+1']}</div>
<div className={styles.voteLabel}>votes</div>
</div>
<div className={styles.issueContent}>
<div className={styles.issueTitle}>{issue.title}</div>
<div className={styles.issueMeta}>
<span className={styles.author}>{issue.user.login}</span>
<span className={styles.separator}></span>
<span className={styles.date}>{formatDate(issue.created_at)}</span>
{issue.comments > 0 && (
<>
<span className={styles.separator}></span>
<span className={styles.comments}>
<MessageSquare size={14} />
{issue.comments}
</span>
</>
)}
</div>
</div>
</a>
))}
</div>
<div className={styles.footer}>
<a
href={`https://github.com/${repo}/issues?q=is%3Aissue+state%3Aopen+label%3A${label}+sort%3Areactions-%2B1-desc`}
target="_blank"
rel="noopener noreferrer"
className={styles.viewAllLink}
>
View all on GitHub
</a>
</div>
</div>
);
}
@@ -0,0 +1,321 @@
.container {
width: 100%;
margin: 20px 0;
border: 1px solid var(--ifm-color-emphasis-300);
border-radius: 8px;
background: var(--ifm-background-surface-color);
overflow: hidden;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 20px;
border-bottom: 1px solid var(--ifm-color-emphasis-300);
}
.title {
margin: 0;
font-size: 18px;
font-weight: 600;
color: var(--ifm-font-color-base);
}
.headerActions {
display: flex;
gap: 8px;
align-items: center;
}
.searchBox {
position: relative;
display: flex;
align-items: center;
}
.searchIcon {
position: absolute;
left: 10px;
color: var(--ifm-color-emphasis-600);
pointer-events: none;
}
.searchInput {
padding: 6px 12px 6px 32px;
border: 1px solid var(--ifm-color-emphasis-300);
border-radius: 6px;
font-size: 14px;
background: var(--ifm-background-surface-color);
color: var(--ifm-font-color-base);
outline: none;
transition: all 0.2s;
width: 200px;
}
.searchInput:focus {
border-color: var(--ifm-color-primary);
box-shadow: 0 0 0 2px rgba(53, 120, 229, 0.1);
}
.searchInput::placeholder {
color: var(--ifm-color-emphasis-600);
}
.sortDropdown {
position: relative;
}
.sortButton,
.newButton {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
border: 1px solid var(--ifm-color-emphasis-300);
background: var(--ifm-background-surface-color);
color: var(--ifm-font-color-base);
cursor: pointer;
transition: all 0.2s;
text-decoration: none;
}
.sortButton:hover,
.newButton:hover {
background: var(--ifm-color-emphasis-100);
border-color: var(--ifm-color-emphasis-400);
}
.sortButton svg,
.newButton svg {
flex-shrink: 0;
}
.sortMenu {
position: absolute;
top: calc(100% + 4px);
right: 0;
min-width: 120px;
background: var(--ifm-background-surface-color);
border: 1px solid var(--ifm-color-emphasis-300);
border-radius: 6px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
z-index: 10;
overflow: hidden;
}
.sortMenuItem,
.sortMenuItemActive {
display: block;
width: 100%;
padding: 8px 12px;
text-align: left;
border: none;
background: none;
color: var(--ifm-font-color-base);
font-size: 14px;
cursor: pointer;
transition: background 0.2s;
}
.sortMenuItem:hover {
background: var(--ifm-color-emphasis-100);
}
.sortMenuItemActive {
background: var(--ifm-color-primary-lightest);
color: var(--ifm-color-primary-dark);
font-weight: 600;
}
.sortMenuItemActive:hover {
background: var(--ifm-color-primary-lighter);
}
.loading,
.error {
text-align: center;
padding: 40px 20px;
}
.error {
color: var(--ifm-color-danger);
}
.errorLink {
display: inline-block;
margin-top: 12px;
color: var(--ifm-color-primary);
text-decoration: none;
font-weight: 600;
}
.errorLink:hover {
text-decoration: underline;
}
.issuesList {
display: flex;
flex-direction: column;
}
.issueRow {
display: flex;
gap: 16px;
padding: 16px 20px;
border-bottom: 1px solid var(--ifm-color-emphasis-200);
text-decoration: none;
color: inherit;
transition: background-color 0.2s;
}
.issueRow:last-child {
border-bottom: none;
}
.issueRow:hover {
background-color: var(--ifm-color-emphasis-100);
}
.voteColumn {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-width: 50px;
flex-shrink: 0;
}
.voteCount {
font-size: 20px;
font-weight: 600;
line-height: 1.2;
color: var(--ifm-font-color-base);
}
.voteLabel {
font-size: 12px;
color: var(--ifm-color-emphasis-600);
margin-top: 2px;
}
.issueContent {
flex: 1;
min-width: 0;
}
.issueTitle {
font-size: 15px;
font-weight: 500;
line-height: 1.4;
color: var(--ifm-font-color-base);
margin-bottom: 6px;
word-wrap: break-word;
}
.issueMeta {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
font-size: 13px;
color: var(--ifm-color-emphasis-700);
}
.author {
color: var(--ifm-color-emphasis-700);
}
.separator {
color: var(--ifm-color-emphasis-500);
}
.date {
color: var(--ifm-color-emphasis-600);
}
.comments {
display: inline-flex;
align-items: center;
gap: 4px;
color: var(--ifm-color-emphasis-700);
}
.comments svg {
flex-shrink: 0;
opacity: 0.7;
}
.label {
font-size: 12px;
padding: 2px 8px;
border-radius: 12px;
background: var(--ifm-color-emphasis-200);
color: var(--ifm-color-emphasis-800);
font-weight: 500;
}
.footer {
padding: 12px 20px;
border-top: 1px solid var(--ifm-color-emphasis-300);
text-align: center;
background: var(--ifm-color-emphasis-50);
}
.viewAllLink {
display: inline-block;
color: var(--ifm-color-primary);
text-decoration: none;
font-weight: 600;
font-size: 14px;
}
.viewAllLink:hover {
text-decoration: underline;
}
/* Responsive */
@media (max-width: 768px) {
.header {
flex-direction: column;
align-items: flex-start;
gap: 12px;
}
.headerActions {
width: 100%;
flex-wrap: wrap;
gap: 8px;
}
.searchBox {
flex: 1;
min-width: 200px;
}
.searchInput {
width: 100%;
}
.issueRow {
flex-direction: column;
gap: 12px;
}
.voteColumn {
flex-direction: row;
gap: 8px;
min-width: auto;
}
.voteLabel::before {
content: ' ';
}
.issueMeta {
font-size: 12px;
}
}
@@ -0,0 +1,75 @@
import React from 'react';
import Link from '@docusaurus/Link';
import clsx from 'clsx';
import styles from './GlossyCard.module.css';
interface GlossyCardProps {
/**
* The title displayed in the card header
*/
title: string;
/**
* Descriptive text for the card content
*/
description: string;
/**
* Color theme for the card ('blue' or 'red')
*/
colorTheme: 'blue' | 'red';
/**
* The URL that the card button will link to
*/
linkPath: string;
/**
* Custom text for the card button (default: "View documentation")
*/
buttonText?: string;
/**
* Additional CSS class names to apply to the card
*/
className?: string;
/**
* Optional icon to display next to the title
*/
icon?: React.ReactNode;
}
export const GlossyCard: React.FC<GlossyCardProps> = ({
title,
description,
colorTheme,
linkPath,
buttonText = 'View documentation',
className,
icon,
}) => {
const colorClass = colorTheme === 'blue' ? 'blueTheme' : 'redTheme';
return (
<div className={clsx(styles.glossyCard, className)}>
<div className={styles.glossyCardContent}>
<div className={styles.cardHeader}>
<div className={clsx(styles.colorBlock, styles[colorClass])}></div>
{icon && <div className={styles.cardIcon}>{icon}</div>}
<h2 className={styles.cardTitle}>{title}</h2>
</div>
<p className={styles.cardDescription}>{description}</p>
<div className={styles.cardAction}>
<Link to={linkPath} className={clsx(styles.cardButton, styles[`${colorClass}Button`])}>
{buttonText} <span className={styles.arrowIcon}></span>
</Link>
</div>
</div>
</div>
);
};
export default GlossyCard;
@@ -0,0 +1,90 @@
import { cx } from 'class-variance-authority';
import type { CSSProperties, PropsWithChildren } from 'react';
export type Variant = 'blue' | 'red' | 'colorful';
type Direction = 'up' | 'down';
function getColors(variant: Variant) {
switch (variant) {
case 'blue':
return {
center: 'oklch(0.7533 0.11 216.4)',
left: 'navy 40%',
right: 'teal 40%',
};
case 'red':
return {
center: 'oklch(0.6 0.22 30.59)',
left: 'black 10%',
right: 'oklch(0.91 0.09 326.28) 40%',
};
case 'colorful':
return {
center: 'var(--color-brand-red)',
left: 'oklch(0.33 0.15 328.37) 80%',
right: 'oklch(0.66 0.17 248.82) 100%',
};
}
}
export function getGradientStyles(
variant: Variant,
direction: Direction = 'up',
isFooter: boolean,
height?: number,
): CSSProperties {
const colors = getColors(variant);
return {
position: 'absolute',
width: '100%',
[isFooter ? 'bottom' : 'top']: 0,
pointerEvents: 'none',
maskComposite: 'intersect',
height,
backgroundImage: `
repeating-linear-gradient(
to right,
rgba(0, 0, 0, 0.05),
rgba(0, 0, 0, 0.25) ${direction === 'down' ? '24px' : '18px'},
transparent 2px,
transparent 10px
),
radial-gradient(
circle at ${direction === 'down' ? 'top' : 'bottom'} center,
${colors.center} 0%,
transparent 60%
),
linear-gradient(to right, color-mix(in srgb, ${colors.center}, ${colors.left}), color-mix(in srgb, ${colors.center}, ${colors.right}))
`,
maskImage: `
${isFooter ? 'radial-gradient(ellipse at center bottom, black 60%, transparent 80%),' : ''}
linear-gradient(to ${direction === 'down' ? 'bottom' : 'top'}, black ${direction === 'down' ? '40%' : '10%'}, transparent ${direction === 'down' ? '90%' : '40%'})
`,
};
}
type Props = PropsWithChildren<{
element?: keyof HTMLElementTagNameMap;
variant: Variant;
direction?: Direction;
isFooter?: boolean;
height?: number;
className?: string;
}>;
export function GradientWrapper({
element: Element = 'div',
variant,
direction = 'up',
isFooter = false,
children,
height,
className,
}: Props) {
return (
<Element className={cx('relative', className)}>
<div style={getGradientStyles(variant, direction, isFooter, height)} />
<div className="z-1">{children}</div>
</Element>
);
}
@@ -0,0 +1,70 @@
import clsx from 'clsx';
import Heading from '@theme/Heading';
import styles from './styles.module.css';
type FeatureItem = {
title: string;
Svg: React.ComponentType<React.ComponentProps<'svg'>>;
description: JSX.Element;
};
const FeatureList: FeatureItem[] = [
{
title: 'Easy to Use',
Svg: require('@site/static/images/undraw_docusaurus_mountain.svg').default,
description: (
<>
Docusaurus was designed from the ground up to be easily installed and used to get your website up and running
quickly.
</>
),
},
{
title: 'Focus on What Matters',
Svg: require('@site/static/images/undraw_docusaurus_tree.svg').default,
description: (
<>
Docusaurus lets you focus on your docs, and we&apos;ll do the chores. Go ahead and move your docs into the{' '}
<code>docs</code> directory.
</>
),
},
{
title: 'Powered by React',
Svg: require('@site/static/images/undraw_docusaurus_react.svg').default,
description: (
<>
Extend or customize your website layout by reusing React. Docusaurus can be extended while reusing the same
header and footer.
</>
),
},
];
function Feature({ title, Svg, description }: FeatureItem) {
return (
<div className={clsx('col col--4')}>
<div className="text--center">
<Svg className={styles.featureSvg} role="img" />
</div>
<div className="text--center padding-horiz--md">
<Heading as="h3">{title}</Heading>
<p>{description}</p>
</div>
</div>
);
}
export default function HomepageFeatures(): JSX.Element {
return (
<section className={styles.features}>
<div className="container">
<div className="row">
{FeatureList.map((props, idx) => (
<Feature key={idx} {...props} />
))}
</div>
</div>
</section>
);
}
@@ -0,0 +1,11 @@
.features {
display: flex;
align-items: center;
padding: 2rem 0;
width: 100%;
}
.featureSvg {
height: 200px;
width: 200px;
}
@@ -0,0 +1,40 @@
.container {
margin: var(--padding-lg) 0;
display: flex;
flex-direction: column;
align-items: center;
}
.imageWrapper {
display: inline-block;
}
.image {
max-width: 100%;
height: auto;
border-radius: 12px;
border: 1px solid var(--ifm-color-gray-300);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
transition: box-shadow 0.2s ease-in-out;
display: block;
}
.image:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
[data-theme='dark'] .image {
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
}
[data-theme='dark'] .image:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
.caption {
margin-top: var(--padding-sm);
font-size: 0.9em;
color: var(--ifm-color-secondary);
text-align: center;
font-style: italic;
}
+22
View File
@@ -0,0 +1,22 @@
import React from 'react';
import styles from './ImageBox.module.css';
import useBaseUrl from '@docusaurus/useBaseUrl';
interface ImageBoxProps {
src: string;
alt: string;
width?: string;
caption?: string;
className?: string;
}
export default function ImageBox({ src, alt, width, caption, className }: ImageBoxProps) {
return (
<div className={`${styles.container} ${className || ''}`}>
<div className={styles.imageWrapper} style={width ? { width } : {}}>
<img src={useBaseUrl(src)} alt={alt} className={styles.image} />
</div>
{caption && <p className={styles.caption}>{caption}</p>}
</div>
);
}
@@ -0,0 +1,15 @@
.docsDropdown {
display: flex;
align-items: center;
gap: 4px;
}
.docsDropdown::after {
top: 4px !important;
}
@media (max-width: 996px) {
.dropdownCircle {
display: none;
}
}
@@ -0,0 +1,123 @@
import useBaseUrl from '@docusaurus/useBaseUrl';
import { useLocation } from '@docusaurus/router';
import DropdownNavbarItem from '@theme/NavbarItem/DropdownNavbarItem';
import styles from './DocsDropdown.module.css';
type Section = 'genai' | 'ml' | 'default';
interface DocsDropdownProps {
mobile?: boolean;
position?: 'left' | 'right';
items: any[];
label?: string;
[key: string]: any;
}
export default function DocsDropdown({
mobile,
items: configItems,
label: configLabel,
...props
}: DocsDropdownProps): JSX.Element {
const location = useLocation();
const getCurrentSection = (): Section => {
const path = location.pathname;
const genaiPath = useBaseUrl('/genai');
const mlPath = useBaseUrl('/ml');
if (path.startsWith(genaiPath)) {
return 'genai';
} else if (path.startsWith(mlPath)) {
return 'ml';
}
return 'default';
};
const currentSection = getCurrentSection();
const getLabel = (): JSX.Element => {
let color;
let text = configLabel || 'Documentation';
if (currentSection === 'genai') {
color = 'var(--genai-color-primary)';
text = 'LLMs & Agents';
} else if (currentSection === 'ml') {
color = 'var(--ml-color-primary)';
text = 'Machine Learning';
}
return (
<div
style={{
display: 'flex',
gap: 8,
alignItems: 'center',
}}
>
{color && (
<div
className={styles.dropdownCircle}
style={{
width: 10,
height: 10,
backgroundColor: color,
borderRadius: 4,
}}
/>
)}
{text}
</div>
);
};
const enhancedItems = configItems.map((item) => {
if (item.docsPluginId === 'classic-ml') {
return {
...item,
label: (
<div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
<div
style={{
width: 10,
height: 10,
backgroundColor: 'var(--ml-color-primary)',
borderRadius: 4,
}}
/>
{item.label}
</div>
),
};
} else if (item.docsPluginId === 'genai') {
return {
...item,
label: (
<div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
<div
style={{
width: 10,
height: 10,
backgroundColor: 'var(--genai-color-primary)',
borderRadius: 4,
}}
/>
{item.label}
</div>
),
};
}
return item;
});
return (
<DropdownNavbarItem
{...props}
mobile={mobile}
label={getLabel()}
items={enhancedItems}
className={styles.docsDropdown}
data-active={currentSection !== 'default' ? currentSection : undefined}
/>
);
}
@@ -0,0 +1,67 @@
import React, { useState, useEffect } from 'react';
import DropdownNavbarItem from '@theme/NavbarItem/DropdownNavbarItem';
import BrowserOnly from '@docusaurus/BrowserOnly';
import { LinkLikeNavbarItemProps } from '@theme/NavbarItem';
interface VersionSelectorProps {
mobile?: boolean;
position?: 'left' | 'right';
label?: string;
[key: string]: any;
}
function getLabel(currentVersion: string, versions: string[]): string {
if (currentVersion === 'latest' && versions.length > 0) {
// version list is sorted in descending order, so the first one is the latest
return `Version: ${versions[0]} (latest)`;
}
return `Version: ${currentVersion}`;
}
function VersionSelectorImpl({ mobile, label = 'Version', ...props }: VersionSelectorProps): JSX.Element {
const [versions, setVersions] = useState<string[]>([]);
const [loading, setLoading] = useState(true);
const versionsUrl = window.location.origin + '/docs/versions.json';
// Determine current version from URL or default to latest
const docPath = window.location.pathname;
const currentVersion = docPath.match(/^\/docs\/([a-zA-Z0-9.]+)/)?.[1];
const versionItems: LinkLikeNavbarItemProps[] = versions?.map((version) => ({
type: 'default',
label: version,
to: window.location.origin + `/docs/${version}/`,
target: '_self',
}));
useEffect(() => {
const fetchVersions = async () => {
try {
const response = await fetch(versionsUrl);
const data = await response.json();
if (data['versions'] != null) {
setVersions(data['versions']);
}
} catch (error) {
// do nothing, this can happen in dev where the versions.json file is not available
} finally {
setLoading(false);
}
};
fetchVersions();
}, [versionsUrl]);
if (loading || versions == null || versions.length === 0) {
return null;
}
return (
<DropdownNavbarItem {...props} mobile={mobile} label={getLabel(currentVersion, versions)} items={versionItems} />
);
}
export default function VersionSelector(props: VersionSelectorProps): JSX.Element {
return <BrowserOnly>{() => <VersionSelectorImpl {...props} />}</BrowserOnly>;
}
@@ -0,0 +1,14 @@
export const NotebookCellOutput = ({ children, isStderr }): JSX.Element => (
<pre
style={{
margin: 0,
borderRadius: 0,
background: 'none',
fontSize: '0.85rem',
flexGrow: 1,
padding: `var(--padding-sm)`,
}}
>
{children}
</pre>
);
@@ -0,0 +1,17 @@
import CodeBlock from '@theme/CodeBlock';
import styles from './styles.module.css';
export const NotebookCodeCell = ({ children, executionCount }): JSX.Element => (
<div
style={{
flexGrow: 1,
minWidth: 0,
marginTop: 'var(--padding-md)',
width: '100%',
}}
>
<CodeBlock className={styles.codeBlock} language="python">
{children}
</CodeBlock>
</div>
);
@@ -0,0 +1,3 @@
.codeBlock {
margin: 0;
}
@@ -0,0 +1,60 @@
import { MouseEvent, ReactNode, useCallback } from 'react';
import { Version } from '@site/src/constants';
interface NotebookDownloadButtonProps {
children: ReactNode;
href: string;
}
export function NotebookDownloadButton({ children, href }: NotebookDownloadButtonProps) {
const handleClick = useCallback(
async (e: MouseEvent) => {
e.preventDefault();
if ((window as any).gtag) {
try {
(window as any).gtag('event', 'notebook-download', {
href,
});
} catch {
// do nothing if the gtag call fails
}
}
if (!Version.includes('dev')) {
// Replace 'master' with the current version to pin the download to the released version
// and avoid 404 errors
href = href.replace(/\/master\//, `/v${Version}/`);
}
const response = await fetch(href);
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.style.display = 'none';
link.href = url;
const filename = href.split('/').pop();
link.download = filename;
document.body.appendChild(link);
link.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(link);
},
[href],
);
return (
<a
className="button button--primary"
style={{ marginBottom: '1rem', display: 'block', width: 'min-content' }}
href={href}
download
onClick={handleClick}
>
{children}
</a>
);
}
@@ -0,0 +1,3 @@
export const NotebookHTMLOutput = ({ children }): JSX.Element => (
<div style={{ flexGrow: 1, minWidth: 0, fontSize: '0.8rem', width: '100%' }}>{children}</div>
);
@@ -0,0 +1,362 @@
import React from 'react';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import Admonition from '@theme/Admonition';
import TilesGrid from '@site/src/components/TilesGrid';
import TileCard from '@site/src/components/TileCard';
import ImageBox from '@site/src/components/ImageBox';
import StepHeader from '@site/src/components/StepHeader';
import ServerSetup from '@site/src/content/setup_server_slim.mdx';
import TabsWrapper from '@site/src/components/TabsWrapper';
import { Users, BookOpen, Scale } from 'lucide-react';
import CodeBlock from '@theme/CodeBlock';
import { getGateway, type OpenAICompatibleGateway } from '@site/src/components/OpenAICompatibleGateways/config';
interface Props {
gatewayId: string;
}
function formatPythonHeaders(headers: Record<string, string>, comment?: string): string {
const entries = Object.entries(headers);
if (entries.length === 0) return '';
const formatted = entries.map(([key, value]) => {
const commentStr = comment ? ` # ${comment}` : '';
return ` "${key}": "${value}",${commentStr}`;
});
return `\n default_headers={\n${formatted.join('\n')}\n },`;
}
function formatPythonHeadersShort(headers: Record<string, string>): string {
const entries = Object.entries(headers);
if (entries.length === 0) return '';
const formatted = entries.map(([key, value]) => `"${key}": "${value}"`).join(', ');
return `, default_headers={${formatted}}`;
}
function formatTypeScriptHeaders(headers: Record<string, string>, comment?: string): string {
const entries = Object.entries(headers);
if (entries.length === 0) return '';
const formatted = entries.map(([key, value]) => {
const commentStr = comment ? ` // ${comment}` : '';
return ` "${key}": "${value}",${commentStr}`;
});
return `\n defaultHeaders: {\n${formatted.join('\n')}\n },`;
}
function formatTypeScriptHeadersShort(headers: Record<string, string>): string {
const entries = Object.entries(headers);
if (entries.length === 0) return '';
const formatted = entries.map(([key, value]) => `"${key}": "${value}"`).join(', ');
return `, defaultHeaders: { ${formatted} }`;
}
export const OpenAICompatibleGatewayDoc: React.FC<Props> = ({ gatewayId }) => {
const gateway = getGateway(gatewayId);
if (!gateway) {
return <div>Gateway "{gatewayId}" not found in configuration.</div>;
}
const displayName = gateway.displayName || gateway.name;
const heroImage = gateway.heroImage || '/images/llms/tracing/basic-openai-trace.png';
const pythonHeaders = gateway.defaultHeaders?.python || {};
const tsHeaders = gateway.defaultHeaders?.typescript || {};
const hasPythonHeaders = Object.keys(pythonHeaders).length > 0;
const hasTsHeaders = Object.keys(tsHeaders).length > 0;
return (
<>
<div dangerouslySetInnerHTML={{ __html: gateway.description }} />
<ImageBox src={heroImage} alt={`${displayName} Tracing`} />
<p>
Since {displayName} exposes an OpenAI-compatible API, you can use MLflow's OpenAI autolog integration to
automatically trace all your LLM calls through the gateway.
</p>
<h2>Getting Started</h2>
<Admonition type="tip" title="Prerequisites">
<div dangerouslySetInnerHTML={{ __html: gateway.prerequisite }} />
</Admonition>
<StepHeader number={1} title="Install Dependencies" />
<TabsWrapper>
<Tabs groupId="programming-language">
<TabItem value="python" label="Python" default>
<CodeBlock language="bash">pip install mlflow openai</CodeBlock>
</TabItem>
<TabItem value="typescript" label="TypeScript">
<CodeBlock language="bash">npm install @mlflow/openai openai</CodeBlock>
</TabItem>
</Tabs>
</TabsWrapper>
<StepHeader number={2} title="Start MLflow Server" />
<ServerSetup />
<StepHeader number={3} title="Enable Tracing and Make API Calls" />
<TabsWrapper>
<Tabs groupId="programming-language">
<TabItem value="python" label="Python" default>
<p>
Enable tracing with <code>mlflow.openai.autolog()</code> and configure the OpenAI client to use{' '}
{displayName}'s base URL.
</p>
<CodeBlock language="python">
{`import mlflow
from openai import OpenAI
# Enable auto-tracing for OpenAI
mlflow.openai.autolog()
# Set tracking URI and experiment
mlflow.set_tracking_uri("http://localhost:5000")
mlflow.set_experiment("${gateway.name}")
# Create OpenAI client pointing to ${displayName}
client = OpenAI(
base_url="${gateway.baseUrl}",
api_key="${gateway.apiKeyPlaceholder}",${hasPythonHeaders ? formatPythonHeaders(pythonHeaders, gateway.headerComment) : ''}
)
# Make API calls - traces will be captured automatically
response = client.chat.completions.create(
model="${gateway.sampleModel}",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What is the capital of France?"},
],
)
print(response.choices[0].message.content)`}
</CodeBlock>
</TabItem>
<TabItem value="typescript" label="TypeScript">
<p>
Initialize MLflow tracing with <code>init()</code> and wrap the OpenAI client with the{' '}
<code>tracedOpenAI</code> function.
</p>
<CodeBlock language="typescript">
{`import { init } from "@mlflow/core";
import { tracedOpenAI } from "@mlflow/openai";
import { OpenAI } from "openai";
// Initialize MLflow tracing
init({
trackingUri: "http://localhost:5000",
experimentId: "<experiment-id>",
});
// Wrap the OpenAI client pointing to ${displayName}
const client = tracedOpenAI(
new OpenAI({
baseURL: "${gateway.baseUrl}",
apiKey: "${gateway.apiKeyPlaceholder}",${hasTsHeaders ? formatTypeScriptHeaders(tsHeaders, gateway.headerComment) : ''}
})
);
// Make API calls - traces will be captured automatically
const response = await client.chat.completions.create({
model: "${gateway.sampleModel}",
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "What is the capital of France?" },
],
});
console.log(response.choices[0].message.content);`}
</CodeBlock>
</TabItem>
</Tabs>
</TabsWrapper>
<StepHeader number={4} title="View Traces in MLflow UI" />
<p>Open the MLflow UI at http://localhost:5000 to see the traces from your {displayName} API calls.</p>
<h2>Combining with Manual Tracing</h2>
<p>
You can combine auto-tracing with MLflow's manual tracing to create comprehensive traces that include your
application logic:
</p>
<TabsWrapper>
<Tabs groupId="programming-language">
<TabItem value="python" label="Python" default>
<CodeBlock language="python">
{`import mlflow
from mlflow.entities import SpanType
from openai import OpenAI
mlflow.openai.autolog()
client = OpenAI(
base_url="${gateway.baseUrl}",
api_key="${gateway.apiKeyPlaceholder}"${hasPythonHeaders ? formatPythonHeadersShort(pythonHeaders) : ''},
)
@mlflow.trace(span_type=SpanType.CHAIN)
def ask_question(question: str) -> str:
"""A traced function that calls the LLM through ${displayName}."""
response = client.chat.completions.create(
model="${gateway.sampleModel}", messages=[{"role": "user", "content": question}]
)
return response.choices[0].message.content
# The entire function call and nested LLM call will be traced
answer = ask_question("What is machine learning?")
print(answer)`}
</CodeBlock>
</TabItem>
<TabItem value="typescript" label="TypeScript">
<CodeBlock language="typescript">
{`import { init, trace, SpanType } from "@mlflow/core";
import { tracedOpenAI } from "@mlflow/openai";
import { OpenAI } from "openai";
init({
trackingUri: "http://localhost:5000",
experimentId: "<experiment-id>",
});
const client = tracedOpenAI(
new OpenAI({
baseURL: "${gateway.baseUrl}",
apiKey: "${gateway.apiKeyPlaceholder}"${hasTsHeaders ? formatTypeScriptHeadersShort(tsHeaders) : ''},
})
);
// Wrap your function with trace() to create a span
const askQuestion = trace(
{ name: "askQuestion", spanType: SpanType.CHAIN },
async (question: string): Promise<string> => {
const response = await client.chat.completions.create({
model: "${gateway.sampleModel}",
messages: [{ role: "user", content: question }],
});
return response.choices[0].message.content ?? "";
}
);
// The entire function call and nested LLM call will be traced
const answer = await askQuestion("What is machine learning?");
console.log(answer);`}
</CodeBlock>
</TabItem>
</Tabs>
</TabsWrapper>
<h2>Streaming Support</h2>
<p>MLflow supports tracing streaming responses from {displayName}:</p>
<TabsWrapper>
<Tabs groupId="programming-language">
<TabItem value="python" label="Python" default>
<CodeBlock language="python">
{`import mlflow
from openai import OpenAI
mlflow.openai.autolog()
client = OpenAI(
base_url="${gateway.baseUrl}",
api_key="${gateway.apiKeyPlaceholder}"${hasPythonHeaders ? formatPythonHeadersShort(pythonHeaders) : ''},
)
stream = client.chat.completions.create(
model="${gateway.sampleModel}",
messages=[{"role": "user", "content": "Write a haiku about machine learning."}],
stream=True,
)
for chunk in stream:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")`}
</CodeBlock>
</TabItem>
<TabItem value="typescript" label="TypeScript">
<CodeBlock language="typescript">
{`import { init } from "@mlflow/core";
import { tracedOpenAI } from "@mlflow/openai";
import { OpenAI } from "openai";
init({
trackingUri: "http://localhost:5000",
experimentId: "<experiment-id>",
});
const client = tracedOpenAI(
new OpenAI({
baseURL: "${gateway.baseUrl}",
apiKey: "${gateway.apiKeyPlaceholder}"${hasTsHeaders ? formatTypeScriptHeadersShort(tsHeaders) : ''},
})
);
const stream = await client.chat.completions.create({
model: "${gateway.sampleModel}",
messages: [{ role: "user", content: "Write a haiku about machine learning." }],
stream: true,
});
for await (const chunk of stream) {
if (chunk.choices[0].delta.content) {
process.stdout.write(chunk.choices[0].delta.content);
}
}`}
</CodeBlock>
</TabItem>
</Tabs>
</TabsWrapper>
<p>MLflow will automatically capture the complete streamed response in the trace.</p>
<h2>Next Steps</h2>
<TilesGrid>
<TileCard
icon={Users}
iconSize={48}
title="Track User Feedback"
description="Record user feedback on traces for tracking user satisfaction."
href="/genai/tracing/collect-user-feedback"
linkText="Learn about feedback →"
containerHeight={64}
/>
<TileCard
icon={BookOpen}
iconSize={48}
title="Manage Prompts"
description="Learn how to manage prompts with MLflow's prompt registry."
href="/genai/prompt-registry"
linkText="Manage prompts "
containerHeight={64}
/>
<TileCard
icon={Scale}
iconSize={48}
title="Evaluate Traces"
description="Evaluate traces with LLM judges to understand and improve your AI application's behavior."
href="/genai/eval-monitor/running-evaluation/traces"
linkText="Evaluate traces "
containerHeight={64}
/>
</TilesGrid>
</>
);
};
export default OpenAICompatibleGatewayDoc;
@@ -0,0 +1,98 @@
export interface OpenAICompatibleGateway {
id: string;
name: string;
displayName?: string; // e.g., "Kong AI Gateway" - if different from name
description: string; // Intro paragraph about the gateway
baseUrl: string;
apiKeyPlaceholder: string;
sampleModel: string;
heroImage?: string; // Optional custom hero image path
defaultHeaders?: {
// For gateways requiring custom headers (e.g., Helicone, Portkey)
python: Record<string, string>;
typescript: Record<string, string>;
};
headerComment?: string; // Comment explaining header usage
prerequisite: string; // HTML content for prerequisite info block
}
export const OPENAI_COMPATIBLE_GATEWAYS: OpenAICompatibleGateway[] = [
{
id: 'openrouter',
name: 'OpenRouter',
description:
'<a href="https://openrouter.ai/">OpenRouter</a> is a unified API gateway that provides access to 280+ LLMs from providers like OpenAI, Anthropic, Google, Meta, and many others through a single OpenAI-compatible API. This allows developers to easily switch between models without changing their code.',
baseUrl: 'https://openrouter.ai/api/v1',
apiKeyPlaceholder: '<YOUR_OPENROUTER_API_KEY>',
sampleModel: 'anthropic/claude-sonnet-4.5',
heroImage: '/images/llms/openrouter/openrouter-tracing.png',
prerequisite:
'Before following the steps below, you need to create an <ins><a href="https://openrouter.ai/">OpenRouter account</a></ins> and generate an API key from the <ins><a href="https://openrouter.ai/keys">Keys page</a></ins>.',
},
{
id: 'vercel-ai-gateway',
name: 'Vercel AI Gateway',
description:
'<a href="https://vercel.com/docs/ai-gateway">Vercel AI Gateway</a> provides a unified API to access hundreds of LLMs through a single endpoint. Key features include high reliability with automatic fallbacks to other providers, spend monitoring across providers, and zero markup on token costs. It works seamlessly with the OpenAI SDK, Anthropic SDK, and Vercel AI SDK.',
baseUrl: 'https://ai-gateway.vercel.sh/v1',
apiKeyPlaceholder: '<YOUR_VERCEL_AI_GATEWAY_API_KEY>',
sampleModel: 'anthropic/claude-sonnet-4.5',
prerequisite:
'Create a <ins><a href="https://vercel.com/">Vercel account</a></ins> and enable <ins><a href="https://vercel.com/docs/ai-gateway">AI Gateway</a></ins> for your project. You can find your API key in the project settings.',
},
{
id: 'truefoundry',
name: 'TrueFoundry',
displayName: 'TrueFoundry AI Gateway',
description:
'<a href="https://www.truefoundry.com/ai-gateway">TrueFoundry AI Gateway</a> is an enterprise-grade LLM gateway that provides access to 1000+ LLMs through a unified OpenAI-compatible API. It offers built-in governance, observability, rate limiting, and cost controls for production AI applications.',
baseUrl: 'https://<your-control-plane>.truefoundry.cloud/api/llm/v1',
apiKeyPlaceholder: '<YOUR_TRUEFOUNDRY_API_KEY>',
sampleModel: 'openai/gpt-4o',
prerequisite:
'Create a <ins><a href="https://www.truefoundry.com/">TrueFoundry account</a></ins> with at least one model provider configured, then generate an API key from the TrueFoundry dashboard.',
},
{
id: 'kong',
name: 'Kong AI Gateway',
description:
'<a href="https://konghq.com/products/kong-ai-gateway">Kong AI Gateway</a> is an enterprise-grade API gateway that provides a unified OpenAI-compatible API to access multiple LLM providers including OpenAI, Anthropic, Azure, AWS Bedrock, Google Gemini, and more. It offers built-in rate limiting, caching, load balancing, and observability.',
baseUrl: 'http://<your-kong-gateway>:8000/v1',
apiKeyPlaceholder: '<YOUR_API_KEY>',
sampleModel: 'gpt-4o',
prerequisite:
'Set up <ins><a href="https://konghq.com/products/kong-ai-gateway">Kong AI Gateway</a></ins> by following the installation guide and configure your LLM provider credentials.',
},
{
id: 'helicone',
name: 'Helicone',
displayName: 'Helicone AI Gateway',
description:
'<a href="https://www.helicone.ai/">Helicone AI Gateway</a> is an open-source LLM gateway that provides unified access to 100+ AI models through an OpenAI-compatible API. It offers built-in caching, rate limiting, automatic failover, and comprehensive analytics with minimal latency overhead.',
baseUrl: 'http://localhost:8080/ai',
apiKeyPlaceholder: 'placeholder-api-key',
sampleModel: 'anthropic/claude-4-5-sonnet',
prerequisite:
'Before following the steps below, you need to set up Helicone AI Gateway server.<ol><li>Set up your <code>.env</code> file with your LLM provider API keys (e.g., <code>OPENAI_API_KEY</code>, <code>ANTHROPIC_API_KEY</code>).</li><li>Run the gateway locally with <code>npx @helicone/ai-gateway@latest</code>.</li></ol>See the <ins><a href="https://docs.helicone.ai/gateway/overview#ai-gateway-overview">Helicone AI Gateway docs</a></ins> for more details.',
},
{
id: 'portkey',
name: 'Portkey',
description:
'<a href="https://portkey.ai/">Portkey</a> is an enterprise-grade AI gateway that provides unified access to 1600+ LLMs through a single OpenAI-compatible API. It offers built-in guardrails, observability, caching, load balancing, and fallback mechanisms for production AI applications.',
baseUrl: 'https://api.portkey.ai/v1',
apiKeyPlaceholder: '<YOUR_PORTKEY_API_KEY>',
sampleModel: 'gpt-4o',
defaultHeaders: {
python: { 'x-portkey-provider': 'openai' },
typescript: { 'x-portkey-provider': 'openai' },
},
headerComment: 'or "anthropic", "google", etc.',
prerequisite:
'Create a <ins><a href="https://portkey.ai/">Portkey account</a></ins> and generate an API key from the <ins><a href="https://app.portkey.ai/api-keys">API Keys page</a></ins>. Configure your virtual keys for the LLM providers you want to use.',
},
];
export function getGateway(id: string): OpenAICompatibleGateway | undefined {
return OPENAI_COMPATIBLE_GATEWAYS.find((g) => g.id === id);
}
@@ -0,0 +1,81 @@
export interface OpenAICompatibleProvider {
id: string;
name: string;
displayName?: string; // e.g., "Kimi (Moonshot AI)" - if different from name
codeRefName?: string; // e.g., "Kimi" - for code comments, defaults to name
baseUrl: string;
tsBaseUrl?: string; // if different from Python
apiKeyPlaceholder: string;
sampleModel: string;
tsSampleModel?: string; // if different in TS example
}
export const OPENAI_COMPATIBLE_PROVIDERS: OpenAICompatibleProvider[] = [
{
id: 'byteplus',
name: 'BytePlus',
baseUrl: 'https://ark.ap-southeast.bytepluses.com/api/v3/',
apiKeyPlaceholder: '<your_byteplus_api_key>',
sampleModel: '<your_byteplus_model>',
},
{
id: 'cohere',
name: 'Cohere',
baseUrl: 'https://api.cohere.ai/compatibility/v1',
tsBaseUrl: 'https://api.cohere.com/v1',
apiKeyPlaceholder: '<your_cohere_api_key>',
sampleModel: 'command-a-03-2025',
tsSampleModel: '<your_cohere_model>',
},
{
id: 'deepseek',
name: 'DeepSeek',
baseUrl: 'https://api.deepseek.com',
apiKeyPlaceholder: '<your_deepseek_api_key>',
sampleModel: 'deepseek-chat',
},
{
id: 'moonshot',
name: 'Moonshot AI',
displayName: 'Kimi (Moonshot AI)',
codeRefName: 'Kimi',
baseUrl: 'https://api.moonshot.ai/v1',
apiKeyPlaceholder: '<MOONSHOT_API_KEY>',
sampleModel: 'moonshot-v1-8k',
},
{
id: 'novitaai',
name: 'Novita AI',
baseUrl: 'https://api.novita.ai/openai',
apiKeyPlaceholder: '<your_novita_api_key>',
sampleModel: 'deepseek/deepseek-r1',
},
{
id: 'qwen',
name: 'Qwen',
displayName: 'Qwen (DashScope)',
baseUrl: 'https://dashscope-intl.aliyuncs.com/compatible-mode/v1',
apiKeyPlaceholder: '<DASHSCOPE_API_KEY>',
sampleModel: 'qwen-plus',
},
{
id: 'togetherai',
name: 'Together AI',
baseUrl: 'https://api.together.xyz/v1',
apiKeyPlaceholder: '<your_together_api_key>',
sampleModel: 'openai/gpt-oss-20b',
},
{
id: 'xai-grok',
name: 'xAI',
displayName: 'xAI / Grok',
codeRefName: 'Grok',
baseUrl: 'https://api.x.ai/v1',
apiKeyPlaceholder: '<your_grok_api_key>',
sampleModel: 'grok-4',
},
];
export function getProvider(id: string): OpenAICompatibleProvider | undefined {
return OPENAI_COMPATIBLE_PROVIDERS.find((p) => p.id === id);
}
@@ -0,0 +1,287 @@
import React from 'react';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import Link from '@docusaurus/Link';
import TilesGrid from '@site/src/components/TilesGrid';
import TileCard from '@site/src/components/TileCard';
import ImageBox from '@site/src/components/ImageBox';
import StepHeader from '@site/src/components/StepHeader';
import ServerSetup from '@site/src/content/setup_server_slim.mdx';
import TabsWrapper from '@site/src/components/TabsWrapper';
import { Users, BookOpen, Scale } from 'lucide-react';
import CodeBlock from '@theme/CodeBlock';
import { getProvider, type OpenAICompatibleProvider } from '@site/src/components/OpenAICompatibleProviders/config';
interface Props {
providerId: string;
}
export const OpenAICompatibleTracingDoc: React.FC<Props> = ({ providerId }) => {
const provider = getProvider(providerId);
if (!provider) {
return <div>Provider "{providerId}" not found in configuration.</div>;
}
const displayName = provider.displayName || provider.name;
const codeRefName = provider.codeRefName || provider.name;
const tsBaseUrl = provider.tsBaseUrl || provider.baseUrl;
const tsSampleModel = provider.tsSampleModel || provider.sampleModel;
return (
<>
<p>
<Link to="../../">MLflow Tracing</Link> provides automatic tracing capability for {displayName} models through
the OpenAI SDK integration. Since {displayName} offers an OpenAI-compatible API format, you can use{' '}
<code>mlflow.openai.autolog()</code> to trace interactions with {codeRefName} models.
</p>
<ImageBox src="/images/llms/tracing/openai-function-calling.png" alt="Tracing via autolog" />
<p>MLflow trace automatically captures the following information about {codeRefName} calls:</p>
<ul>
<li>Prompts and completion responses</li>
<li>Latencies</li>
<li>Token usage</li>
<li>Model name</li>
<li>
Additional metadata such as <code>temperature</code>, <code>max_completion_tokens</code>, if specified.
</li>
<li>Function calling if returned in the response</li>
<li>Built-in tools such as web search, file search, computer use, etc.</li>
<li>Any exception if raised</li>
</ul>
<h2>Getting Started</h2>
<StepHeader number={1} title="Install dependencies" />
<TabsWrapper>
<Tabs>
<TabItem value="python" label="Python" default>
<CodeBlock language="bash">pip install mlflow openai</CodeBlock>
</TabItem>
<TabItem value="typescript" label="JS / TS">
<CodeBlock language="bash">npm install @mlflow/openai openai</CodeBlock>
</TabItem>
</Tabs>
</TabsWrapper>
<StepHeader number={2} title="Start MLflow server" />
<ServerSetup />
<StepHeader number={3} title={`Enable tracing and call ${codeRefName}`} />
<TabsWrapper>
<Tabs>
<TabItem value="python" label="Python" default>
<CodeBlock language="python">
{`import openai
import mlflow
# Enable auto-tracing for OpenAI (works with ${codeRefName})
mlflow.openai.autolog()
# Optional: Set a tracking URI and an experiment
mlflow.set_tracking_uri("http://localhost:5000")
mlflow.set_experiment("${codeRefName}")
# Initialize the OpenAI client with ${codeRefName} API endpoint
client = openai.OpenAI(
base_url="${provider.baseUrl}",
api_key="${provider.apiKeyPlaceholder}",
)
response = client.chat.completions.create(
model="${provider.sampleModel}",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What is the capital of France?"},
],
)`}
</CodeBlock>
</TabItem>
<TabItem value="typescript" label="JS / TS">
<CodeBlock language="typescript">
{`import { OpenAI } from "openai";
import { tracedOpenAI } from "@mlflow/openai";
// Wrap the OpenAI client and point to ${codeRefName} endpoint
const client = tracedOpenAI(
new OpenAI({
baseURL: "${tsBaseUrl}",
apiKey: "${provider.apiKeyPlaceholder}",
})
);
const response = await client.chat.completions.create({
model: "${tsSampleModel}",
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "What is the capital of France?" },
],
temperature: 0.1,
max_tokens: 100,
});`}
</CodeBlock>
</TabItem>
</Tabs>
</TabsWrapper>
<StepHeader number={4} title="View traces in MLflow UI" />
<p>
Browse to your MLflow UI (for example, http://localhost:5000) and open the <code>{codeRefName}</code> experiment
to see traces for the calls above.
</p>
<ImageBox src="/images/llms/tracing/basic-openai-trace.png" alt={`${codeRefName} Tracing`} />
<p>
-&gt; View{' '}
<u>
<a href="#next-steps">Next Steps</a>
</u>{' '}
for learning about more MLflow features like user feedback tracking, prompt management, and evaluation.
</p>
<h2>Streaming and Async Support</h2>
<p>
MLflow supports tracing for streaming and async {codeRefName} APIs. Visit the{' '}
<Link to="../openai">OpenAI Tracing documentation</Link> for example code snippets for tracing streaming and
async calls through OpenAI SDK.
</p>
<h2>Combine with frameworks or manual tracing</h2>
<p>
The automatic tracing capability in MLflow is designed to work seamlessly with the{' '}
<Link to="/genai/tracing/app-instrumentation/manual-tracing">Manual Tracing SDK</Link> or multi-framework
integrations. The examples below show Python (manual span) and JS/TS (manual span) at the same level of
complexity.
</p>
<TabsWrapper>
<Tabs>
<TabItem value="python" label="Python" default>
<CodeBlock language="python">
{`import json
from openai import OpenAI
import mlflow
from mlflow.entities import SpanType
# Initialize the OpenAI client with ${codeRefName} API endpoint
client = OpenAI(
base_url="${provider.baseUrl}",
api_key="${provider.apiKeyPlaceholder}",
)
# Create a parent span for the ${codeRefName} call
@mlflow.trace(span_type=SpanType.CHAIN)
def answer_question(question: str):
messages = [{"role": "user", "content": question}]
response = client.chat.completions.create(
model="${provider.sampleModel}",
messages=messages,
)
# Attach session/user metadata to the trace
mlflow.update_current_trace(
metadata={
"mlflow.trace.session": "session-12345",
"mlflow.trace.user": "user-a",
}
)
return response.choices[0].message.content
answer = answer_question("What is the capital of France?")`}
</CodeBlock>
</TabItem>
<TabItem value="typescript" label="JS / TS">
<CodeBlock language="typescript">
{`import * as mlflow from "@mlflow/core";
import { OpenAI } from "openai";
import { tracedOpenAI } from "@mlflow/openai";
mlflow.init({
trackingUri: "http://localhost:5000",
experimentId: "<your_experiment_id>",
});
// Wrap the OpenAI client and point to ${codeRefName} endpoint
const client = tracedOpenAI(
new OpenAI({
baseURL: "${tsBaseUrl}",
apiKey: "${provider.apiKeyPlaceholder}",
})
);
// Create a traced function that wraps the ${codeRefName} call
const answerQuestion = mlflow.trace(
async (question: string) => {
const resp = await client.chat.completions.create({
model: "${tsSampleModel}",
messages: [{ role: "user", content: question }],
});
return resp.choices[0].message?.content;
},
{ name: "answer-question" }
);
await answerQuestion("What is the capital of France?");`}
</CodeBlock>
</TabItem>
</Tabs>
</TabsWrapper>
<p>
Running either example will produce a trace that includes the {codeRefName} LLM span; the traced function
creates the parent span automatically.
</p>
<ImageBox
src="/images/llms/tracing/openai-trace-with-manual-span.png"
alt={`${codeRefName} Tracing with Manual Tracing`}
/>
<h2 id="next-steps">Next steps</h2>
<TilesGrid>
<TileCard
icon={Users}
iconSize={48}
title="Track User Feedback"
description="Record user feedback on traces for tracking user satisfaction."
href="/genai/tracing/collect-user-feedback"
linkText="Learn about feedback ->"
containerHeight={64}
/>
<TileCard
icon={BookOpen}
iconSize={48}
title="Manage Prompts"
description="Learn how to manage prompts with MLflow's prompt registry."
href="/genai/prompt-registry"
linkText="Manage prompts ->"
containerHeight={64}
/>
<TileCard
icon={Scale}
iconSize={48}
title="Evaluate Traces"
description="Evaluate traces with LLM judges to understand and improve your AI application's behavior."
href="/genai/eval-monitor/running-evaluation/traces"
linkText="Evaluate traces ->"
containerHeight={64}
/>
</TilesGrid>
</>
);
};
export default OpenAICompatibleTracingDoc;
+38
View File
@@ -0,0 +1,38 @@
import React, { useState, useEffect } from 'react';
import { Search } from 'lucide-react';
import clsx from 'clsx';
import styles from './styles.module.css';
export interface SearchBoxProps {
/**
* Placeholder text for the button
*/
placeholder?: string;
/**
* Additional CSS classes
*/
className?: string;
}
export default function SearchBox({
placeholder = 'What do you want to learn?',
className,
}: SearchBoxProps): JSX.Element {
const openRunLLM = () => {
// Open RunLLM widget when user clicks the input
if (typeof window !== 'undefined' && (window as any).runllm) {
(window as any).runllm.open();
}
};
return (
<div className={clsx(styles.searchContainer, className)}>
<div className={styles.searchWrapper}>
<input type="text" className={styles.searchInput} placeholder={placeholder} onClick={openRunLLM} readOnly />
<button type="button" className={styles.searchButton} onClick={openRunLLM}>
<Search size={20} />
</button>
</div>
</div>
);
}
@@ -0,0 +1,61 @@
.searchContainer {
position: relative;
max-width: 500px;
width: 100%;
}
.searchWrapper {
position: relative;
display: flex;
align-items: center;
}
.searchInput {
width: 100%;
padding: 1rem 3rem 1rem 1.5rem;
border: 2px solid var(--ifm-border-color);
border-radius: 8px;
font-size: 1rem;
background: var(--ifm-background-color);
color: var(--ifm-color-emphasis-hover);
transition: all 0.2s ease;
opacity: 0.7;
}
.searchInput:focus {
outline: none;
border-color: #0194e2;
box-shadow: 0 0 0 3px rgba(1, 148, 226, 0.1);
opacity: 1;
}
.searchInput::placeholder {
color: var(--ifm-border-color);
}
.searchButton {
position: absolute;
right: 0.5rem;
padding: 0.5rem;
background: transparent;
border: none;
color: var(--ifm-border-color);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: color 0.2s ease;
border-radius: 4px;
}
.searchButton:hover:not(:disabled) {
color: #0194e2;
background: rgba(1, 148, 226, 0.1);
}
/* Hide SearchBox on mobile devices */
@media (max-width: 768px) {
.searchContainer {
display: none;
}
}
+18
View File
@@ -0,0 +1,18 @@
import React from 'react';
import styles from './styles.module.css';
interface StepHeaderProps {
number: number;
title: string;
}
const StepHeader: React.FC<StepHeaderProps> = ({ number, title }) => {
return (
<div className={styles.stepHeader}>
<div className={styles.stepNumber}>{number}</div>
<h3 className={styles.stepTitle}>{title}</h3>
</div>
);
};
export default StepHeader;
@@ -0,0 +1,31 @@
.stepHeader {
display: flex;
align-items: center;
gap: 1rem;
margin: 2rem 0 1rem 0;
}
.stepNumber {
display: flex;
align-items: center;
justify-content: center;
width: 34px;
height: 34px;
min-width: 34px;
border-radius: 50%;
background: transparent;
box-shadow:
0 0 0 2px rgba(1, 148, 226, 0.6),
0 3px 8px rgba(1, 148, 226, 0.14);
color: #0194e2;
font-weight: 600;
font-size: 1.1rem;
flex-shrink: 0;
}
.stepTitle {
margin: 0;
font-size: 1.4rem;
font-weight: 400;
color: var(--ifm-color-emphasis-900);
}
+9
View File
@@ -0,0 +1,9 @@
import { PropsWithChildren } from 'react';
export function Table({ children }: PropsWithChildren) {
return (
<div className="w-full overflow-x-auto">
<table>{children}</table>
</div>
);
}
@@ -0,0 +1,25 @@
.wrapper {
padding: var(--padding-lg);
border: 1px solid var(--ifm-color-gray-400);
border-radius: 12px;
margin: var(--padding-lg) 0;
background: var(--ifm-background-surface-color);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
transition: box-shadow 0.2s ease-in-out;
}
.wrapper:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
[data-theme='dark'] .wrapper {
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
}
[data-theme='dark'] .wrapper:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
.wrapper img {
border-radius: 12px;
}
+10
View File
@@ -0,0 +1,10 @@
import React from 'react';
import styles from './TabsWrapper.module.css';
interface TabsWrapperProps {
children: React.ReactNode;
}
export default function TabsWrapper({ children }: TabsWrapperProps) {
return <div className={styles.wrapper}>{children}</div>;
}
+111
View File
@@ -0,0 +1,111 @@
import React from 'react';
import Link from '@docusaurus/Link';
import clsx from 'clsx';
import { LucideIcon } from 'lucide-react';
import styles from './styles.module.css';
import useBaseUrl from '@docusaurus/useBaseUrl';
import ThemedImage from '@theme/ThemedImage';
export interface TileCardProps {
/**
* The icon component to display at the top of the card (optional if image is provided)
*/
icon?: LucideIcon;
/**
* The image source to display at the top of the card (optional if icon is provided)
*/
image?: string;
/**
* The dark mode image source (optional, defaults to image if not provided)
*/
imageDark?: string;
/**
* The width of the image in pixels (optional)
*/
imageWidth?: number;
/**
* The height of the image in pixels (optional)
*/
imageHeight?: number;
/**
* The size of the icon (default: 32) - only used when icon is provided
*/
iconSize?: number;
/**
* The height of the icon/image container in pixels (optional)
*/
containerHeight?: number;
/**
* The title of the card
*/
title: string;
/**
* The description text
*/
description: string;
/**
* The href for the link
*/
href: string;
/**
* The link text (default: "Learn more →")
*/
linkText?: string;
/**
* Additional CSS classes for the card
*/
className?: string;
}
/**
* A reusable tile card component for displaying feature cards with icon, title, description and link
*/
export default function TileCard({
icon: Icon,
image,
imageDark,
imageWidth,
imageHeight,
iconSize = 32,
containerHeight,
title,
description,
href,
linkText = 'Learn more →',
className,
}: TileCardProps): JSX.Element {
// Ensure either icon or image is provided
if (!Icon && !image) {
throw new Error('TileCard requires either an icon or image prop');
}
const containerStyle = containerHeight ? { height: `${containerHeight}px` } : {};
const imageStyle: React.CSSProperties = {};
if (imageWidth) imageStyle.width = `${imageWidth}px`;
if (imageHeight) imageStyle.height = `${imageHeight}px`;
return (
<Link href={href} className={clsx(styles.tileCard, className)}>
<div className={styles.tileIcon} style={containerStyle}>
{Icon ? (
<Icon size={iconSize} />
) : imageDark ? (
<ThemedImage
sources={{
light: useBaseUrl(image),
dark: useBaseUrl(imageDark),
}}
alt={title}
className={styles.tileImage}
style={imageStyle}
/>
) : (
<img src={useBaseUrl(image)} alt={title} className={styles.tileImage} style={imageStyle} />
)}
</div>
<h3>{title}</h3>
<p>{description}</p>
<div className={styles.tileLink}>{linkText}</div>
</Link>
);
}
@@ -0,0 +1,108 @@
.tileCard {
background: var(--ifm-background-color);
border: 1px solid var(--ifm-border-color);
border-radius: 12px;
padding: 2rem;
text-align: center;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.tileCard:hover {
transform: translateY(-4px);
box-shadow: var(--ifm-shadow-tile-hover);
border-color: #0194e2;
}
.tileCard:hover .tileIcon {
transform: scale(1.1);
}
.tileIcon {
margin-bottom: 1rem;
transition: transform 0.3s ease;
display: flex;
justify-content: center;
align-items: center;
color: #0194e2;
}
.tileCard h3 {
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 0.5rem;
color: var(--ifm-color-emphasis-hover);
}
.tileCard p {
color: var(--ifm-border-color);
margin-bottom: 1.5rem;
font-size: 0.9rem;
}
.tileLink {
display: inline-flex;
align-items: center;
gap: 0.5rem;
color: #0194e2;
text-decoration: none;
font-weight: 500;
font-size: 0.9rem;
transition: color 0.2s ease;
}
.tileLink:hover {
color: #0066cc;
text-decoration: none;
}
.tileImage {
max-width: 100%;
max-height: 100%;
height: auto;
object-fit: contain;
background: transparent;
}
/* Add subtle background for X logo for better contrast */
.tileCard[href*='x.com'] .tileImage {
background: rgba(128, 128, 128, 0.1);
border-radius: 8px;
padding: 6px;
}
/* Dark mode specific styling for X logo */
[data-theme='dark'] .tileCard[href*='x.com'] .tileImage {
background: rgba(255, 255, 255, 0.08);
}
/* Dark mode adjustments for deep learning framework logos */
/* PyTorch logo - has black text, needs inversion */
[data-theme='dark'] .tileCard[href*='pytorch'] .tileImage {
filter: invert(1) hue-rotate(180deg);
background: transparent;
}
/* TensorFlow logo - has dark text, needs inversion */
[data-theme='dark'] .tileCard[href*='tensorflow'] .tileImage {
filter: invert(1) hue-rotate(180deg);
background: transparent;
}
/* spaCy logo - bright blue, just needs brightening */
[data-theme='dark'] .tileCard[href*='spacy'] .tileImage {
filter: brightness(1.3);
background: transparent;
}
/* HuggingFace/Transformers logo - now using proper dark mode SVG */
[data-theme='dark'] .tileCard[href*='transformers'] .tileImage {
background: transparent;
}
/* Sentence Transformers logo */
[data-theme='dark'] .tileCard[href*='sentence-transformers'] .tileImage {
filter: brightness(1.5);
background: transparent;
}
+12
View File
@@ -0,0 +1,12 @@
import React from 'react';
import clsx from 'clsx';
import styles from './styles.module.css';
export interface TilesGridProps {
children: React.ReactNode;
className?: string;
}
export default function TilesGrid({ children, className }: TilesGridProps): JSX.Element {
return <div className={clsx(styles.tilesGrid, className)}>{children}</div>;
}
@@ -0,0 +1,23 @@
.tilesGrid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 2rem;
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
@media (max-width: 996px) {
.tilesGrid {
grid-template-columns: repeat(2, 1fr);
gap: 1.5rem;
}
}
@media (max-width: 768px) {
.tilesGrid {
grid-template-columns: 1fr;
gap: 1rem;
padding: 1rem;
}
}
@@ -0,0 +1,32 @@
import React from 'react';
import useBaseUrl from '@docusaurus/useBaseUrl';
interface OTelIntegrationCardProps {
name: string;
logoPath: string;
}
export const OTelIntegrationCard: React.FC<OTelIntegrationCardProps> = ({ name, logoPath }) => (
<div
style={{
display: 'flex',
alignItems: 'center',
gap: '1rem',
padding: '1rem',
backgroundColor: 'var(--ifm-color-emphasis-100)',
borderRadius: '8px',
marginBottom: '1.5rem',
}}
>
<img src={useBaseUrl(logoPath)} alt={`${name} Logo`} style={{ height: '36px' }} />
<div>
<h4 style={{ margin: 0, marginBottom: '0.5rem' }}>Integration via OpenTelemetry</h4>
<p style={{ margin: 0 }}>
{name} can be integrated with MLflow via OpenTelemetry. Configure {name}'s OpenTelemetry exporter to send traces
to MLflow's OTLP endpoint.
</p>
</div>
</div>
);
export default OTelIntegrationCard;
@@ -0,0 +1,635 @@
import React from 'react';
import { CardGroup, SmallLogoCard } from '../Card';
import useBaseUrl from '@docusaurus/useBaseUrl';
interface TracingIntegration {
id: string;
name: string;
logoPath: string;
link: string;
category: string;
}
interface TracingIntegrationsProps {
cardGroupProps?: {
isSmall?: boolean;
cols?: number;
noGap?: boolean;
};
category?: Category;
}
type Category =
| 'OpenTelemetry'
| 'Agent Frameworks (Python)'
| 'Agent Frameworks (TypeScript)'
| 'Agent Frameworks (Java)'
| 'Coding Agents & Long-Running Agents'
| 'Model Providers'
| 'Tools'
| 'Gateways'
| 'No-Code';
const CATEGORY_ORDER: Category[] = [
'OpenTelemetry',
'Agent Frameworks (Python)',
'Agent Frameworks (TypeScript)',
'Agent Frameworks (Java)',
'Coding Agents & Long-Running Agents',
'Model Providers',
'Tools',
'Gateways',
'No-Code',
];
// Centralized integration definitions with categories
const TRACING_INTEGRATIONS: TracingIntegration[] = [
// OpenTelemetry
{
id: 'opentelemetry',
name: 'OpenTelemetry',
logoPath: '/images/logos/opentelemetry-logo-only.png',
link: '/genai/tracing/app-instrumentation/opentelemetry',
category: 'OpenTelemetry',
},
// Agent Frameworks (Python)
{
id: 'langchain',
name: 'LangChain',
logoPath: '/images/logos/langchain-logo-only.png',
link: '/genai/tracing/integrations/listing/langchain',
category: 'Agent Frameworks (Python)',
},
{
id: 'langgraph',
name: 'LangGraph',
logoPath: '/images/logos/langgraph-logo-only.png',
link: '/genai/tracing/integrations/listing/langgraph',
category: 'Agent Frameworks (Python)',
},
{
id: 'openai-agent',
name: 'OpenAI Agent',
logoPath: '/images/logos/openai-logo-only.png',
link: '/genai/tracing/integrations/listing/openai-agent',
category: 'Agent Frameworks (Python)',
},
{
id: 'dspy',
name: 'DSPy',
logoPath: '/images/logos/dspy-logo.png',
link: '/genai/tracing/integrations/listing/dspy',
category: 'Agent Frameworks (Python)',
},
{
id: 'pydantic_ai',
name: 'PydanticAI',
logoPath: '/images/logos/pydantic-ai-logo-only.png',
link: '/genai/tracing/integrations/listing/pydantic_ai',
category: 'Agent Frameworks (Python)',
},
{
id: 'google-adk',
name: 'Google ADK',
logoPath: '/images/logos/google-adk-logo.png',
link: '/genai/tracing/integrations/listing/google-adk',
category: 'Agent Frameworks (Python)',
},
{
id: 'claude_agent_sdk_python',
name: 'Claude Agent SDK',
logoPath: '/images/logos/claude-code-logo.png',
link: '/genai/tracing/integrations/listing/claude_agent_sdk_python',
category: 'Agent Frameworks (Python)',
},
{
id: 'microsoft-agent-framework',
name: 'Microsoft Agent Framework',
logoPath: '/images/logos/microsoft-agent-framework-logo.png',
link: '/genai/tracing/integrations/listing/microsoft-agent-framework',
category: 'Agent Frameworks (Python)',
},
{
id: 'crewai',
name: 'CrewAI',
logoPath: '/images/logos/crewai-logo.svg',
link: '/genai/tracing/integrations/listing/crewai',
category: 'Agent Frameworks (Python)',
},
{
id: 'llama_index',
name: 'LlamaIndex',
logoPath: '/images/logos/llamaindex-logo.svg',
link: '/genai/tracing/integrations/listing/llama_index',
category: 'Agent Frameworks (Python)',
},
{
id: 'autogen',
name: 'AutoGen',
logoPath: '/images/logos/autogen-logo.png',
link: '/genai/tracing/integrations/listing/autogen',
category: 'Agent Frameworks (Python)',
},
{
id: 'strands',
name: 'Strands Agent SDK',
logoPath: '/images/logos/strands-logo.png',
link: '/genai/tracing/integrations/listing/strands',
category: 'Agent Frameworks (Python)',
},
{
id: 'livekit',
name: 'LiveKit Agents',
logoPath: '/images/logos/livekit-logo.png',
link: '/genai/tracing/integrations/listing/livekit',
category: 'Agent Frameworks (Python)',
},
{
id: 'agno',
name: 'Agno',
logoPath: '/images/logos/agno-logo.png',
link: '/genai/tracing/integrations/listing/agno',
category: 'Agent Frameworks (Python)',
},
{
id: 'bedrock-agentcore',
name: 'Amazon Bedrock AgentCore',
logoPath: '/images/logos/bedrock-logo.png',
link: '/genai/tracing/integrations/listing/bedrock-agentcore',
category: 'Agent Frameworks (Python)',
},
{
id: 'smolagents',
name: 'Smolagents',
logoPath: '/images/logos/smolagents-logo.png',
link: '/genai/tracing/integrations/listing/smolagents',
category: 'Agent Frameworks (Python)',
},
{
id: 'semantic_kernel',
name: 'Semantic Kernel',
logoPath: '/images/logos/semantic-kernel-logo.png',
link: '/genai/tracing/integrations/listing/semantic_kernel',
category: 'Agent Frameworks (Python)',
},
{
id: 'deepagent',
name: 'LangChain DeepAgent',
logoPath: '/images/logos/deepagent-logo.svg',
link: '/genai/tracing/integrations/listing/deepagent',
category: 'Agent Frameworks (Python)',
},
{
id: 'ag2',
name: 'AG2',
logoPath: '/images/logos/ag2-logo.png',
link: '/genai/tracing/integrations/listing/ag2',
category: 'Agent Frameworks (Python)',
},
{
id: 'haystack',
name: 'Haystack',
logoPath: '/images/logos/haystack-logo.png',
link: '/genai/tracing/integrations/listing/haystack',
category: 'Agent Frameworks (Python)',
},
{
id: 'koog',
name: 'Koog',
logoPath: '/images/logos/koog.png',
link: '/genai/tracing/integrations/listing/koog',
category: 'Agent Frameworks (Python)',
},
{
id: 'txtai',
name: 'txtai',
logoPath: '/images/logos/txtai-logo.png',
link: '/genai/tracing/integrations/listing/txtai',
category: 'Agent Frameworks (Python)',
},
{
id: 'pipecat',
name: 'Pipecat',
logoPath: '/images/logos/pipecat.png',
link: '/genai/tracing/integrations/listing/pipecat',
category: 'Agent Frameworks (Python)',
},
{
id: 'watsonx-orchestrate',
name: 'Watsonx Orchestrate',
logoPath: '/images/logos/watsonx-orchestrate.png',
link: '/genai/tracing/integrations/listing/watsonx-orchestrate',
category: 'Agent Frameworks (Python)',
},
// Agent Frameworks (TypeScript)
{
id: 'langchain-ts',
name: 'LangChain',
logoPath: '/images/logos/langchain-logo-only.png',
link: '/genai/tracing/integrations/listing/langchain',
category: 'Agent Frameworks (TypeScript)',
},
{
id: 'langgraph-ts',
name: 'LangGraph',
logoPath: '/images/logos/langgraph-logo-only.png',
link: '/genai/tracing/integrations/listing/langgraph',
category: 'Agent Frameworks (TypeScript)',
},
{
id: 'vercelai',
name: 'Vercel AI SDK',
logoPath: '/images/logos/vercel-logo.svg',
link: '/genai/tracing/integrations/listing/vercelai',
category: 'Agent Frameworks (TypeScript)',
},
{
id: 'mastra',
name: 'Mastra',
logoPath: '/images/logos/mastra-logo.png',
link: '/genai/tracing/integrations/listing/mastra',
category: 'Agent Frameworks (TypeScript)',
},
{
id: 'voltagent',
name: 'VoltAgent',
logoPath: '/images/logos/voltagent-logo.png',
link: '/genai/tracing/integrations/listing/voltagent',
category: 'Agent Frameworks (TypeScript)',
},
{
id: 'claude_agent_sdk_typescript',
name: 'Claude Agent SDK (TS)',
logoPath: '/images/logos/claude-code-logo.png',
link: '/genai/tracing/integrations/listing/claude_agent_sdk_typescript',
category: 'Agent Frameworks (TypeScript)',
},
{
id: 'spring-ai',
name: 'Spring AI',
logoPath: '/images/logos/spring-ai-logo.png',
link: '/genai/tracing/integrations/listing/spring-ai',
category: 'Agent Frameworks (Java)',
},
{
id: 'quarkus-langchain4j',
name: 'Quarkus LangChain4j',
logoPath: '/images/logos/langchain4j.svg',
link: '/genai/tracing/integrations/listing/quarkus-langchain4j',
category: 'Agent Frameworks (Java)',
},
// Model Providers
{
id: 'openai',
name: 'OpenAI',
logoPath: '/images/logos/openai-logo-only.png',
link: '/genai/tracing/integrations/listing/openai',
category: 'Model Providers',
},
{
id: 'anthropic',
name: 'Anthropic',
logoPath: '/images/logos/anthropic-logo.png',
link: '/genai/tracing/integrations/listing/anthropic',
category: 'Model Providers',
},
{
id: 'databricks',
name: 'Databricks',
logoPath: '/images/logos/databricks-logo.png',
link: '/genai/tracing/integrations/listing/databricks',
category: 'Model Providers',
},
{
id: 'gemini',
name: 'Gemini',
logoPath: '/images/logos/google-gemini-logo.svg',
link: '/genai/tracing/integrations/listing/gemini',
category: 'Model Providers',
},
{
id: 'bedrock',
name: 'Amazon Bedrock',
logoPath: '/images/logos/bedrock-logo.png',
link: '/genai/tracing/integrations/listing/bedrock',
category: 'Model Providers',
},
{
id: 'litellm',
name: 'LiteLLM',
logoPath: '/images/logos/litellm-logo.png',
link: '/genai/tracing/integrations/listing/litellm',
category: 'Model Providers',
},
{
id: 'mistral',
name: 'Mistral',
logoPath: '/images/logos/mistral-ai-logo.svg',
link: '/genai/tracing/integrations/listing/mistral',
category: 'Model Providers',
},
{
id: 'xai-grok',
name: 'xAI / Grok',
logoPath: '/images/logos/grok-logo.png',
link: '/genai/tracing/integrations/listing/xai-grok',
category: 'Model Providers',
},
{
id: 'ollama',
name: 'Ollama',
logoPath: '/images/logos/ollama-logo.png',
link: '/genai/tracing/integrations/listing/ollama',
category: 'Model Providers',
},
{
id: 'groq',
name: 'Groq',
logoPath: '/images/logos/groq-logo.svg',
link: '/genai/tracing/integrations/listing/groq',
category: 'Model Providers',
},
{
id: 'deepseek',
name: 'DeepSeek',
logoPath: '/images/logos/deepseek-logo.png',
link: '/genai/tracing/integrations/listing/deepseek',
category: 'Model Providers',
},
{
id: 'qwen',
name: 'Qwen',
logoPath: '/images/logos/qwen-logo.jpg',
link: '/genai/tracing/integrations/listing/qwen',
category: 'Model Providers',
},
{
id: 'moonshot',
name: 'Moonshot AI',
logoPath: '/images/logos/kimi-logo.png',
link: '/genai/tracing/integrations/listing/moonshot',
category: 'Model Providers',
},
{
id: 'cohere',
name: 'Cohere',
logoPath: '/images/logos/cohere-logo.png',
link: '/genai/tracing/integrations/listing/cohere',
category: 'Model Providers',
},
{
id: 'byteplus',
name: 'BytePlus',
logoPath: '/images/logos/byteplus-logo.png',
link: '/genai/tracing/integrations/listing/byteplus',
category: 'Model Providers',
},
{
id: 'novitaai',
name: 'Novita AI',
logoPath: '/images/logos/novitaai-logo.jpg',
link: '/genai/tracing/integrations/listing/novitaai',
category: 'Model Providers',
},
{
id: 'fireworksai',
name: 'FireworksAI',
logoPath: '/images/logos/fireworks-ai-logo.png',
link: '/genai/tracing/integrations/listing/fireworksai',
category: 'Model Providers',
},
{
id: 'togetherai',
name: 'Together AI',
logoPath: '/images/logos/together-ai-logo.png',
link: '/genai/tracing/integrations/listing/togetherai',
category: 'Model Providers',
},
// Coding Agents & Long-Running Agents
{
id: 'claude_code',
name: 'Claude Code',
logoPath: '/images/logos/claude-code-icon.png',
link: '/genai/tracing/integrations/listing/claude_code',
category: 'Coding Agents & Long-Running Agents',
},
{
id: 'opencode',
name: 'OpenCode',
logoPath: '/images/logos/opencode-logo.png',
link: '/genai/tracing/integrations/listing/opencode',
category: 'Coding Agents & Long-Running Agents',
},
{
id: 'openhands',
name: 'OpenHands',
logoPath: '/images/logos/openhands-logo.png',
link: '/genai/tracing/integrations/listing/openhands',
category: 'Coding Agents & Long-Running Agents',
},
{
id: 'codex',
name: 'Codex CLI',
logoPath: '/images/logos/openai-logo-only.png',
link: '/genai/tracing/integrations/listing/codex',
category: 'Coding Agents & Long-Running Agents',
},
{
id: 'gemini_cli',
name: 'Gemini CLI',
logoPath: '/images/logos/google-gemini-logo.svg',
link: '/genai/tracing/integrations/listing/gemini_cli',
category: 'Coding Agents & Long-Running Agents',
},
{
id: 'hermes_agent',
name: 'Hermes Agent',
logoPath: '/images/logos/hermes-agent-logo.svg',
link: '/genai/tracing/integrations/listing/hermes_agent',
category: 'Coding Agents & Long-Running Agents',
},
{
id: 'omnigent',
name: 'Omnigent',
logoPath: '/images/logos/omnigent-logo.png',
link: '/genai/tracing/integrations/listing/omnigent',
category: 'Coding Agents & Long-Running Agents',
},
{
id: 'qwen_code',
name: 'Qwen Code',
logoPath: '/images/logos/qwen-logo.jpg',
link: '/genai/tracing/integrations/listing/qwen_code',
category: 'Coding Agents & Long-Running Agents',
},
{
id: 'instructor',
name: 'Instructor',
logoPath: '/images/logos/instructor-logo.svg',
link: '/genai/tracing/integrations/listing/instructor',
category: 'Tools',
},
{
id: 'langfuse',
name: 'Langfuse',
logoPath: '/images/logos/langfuse-logo.png',
link: '/genai/tracing/integrations/listing/langfuse',
category: 'Tools',
},
{
id: 'arize-phoenix',
name: 'Arize / Phoenix',
logoPath: '/images/logos/arize-phoenix-logo.png',
link: '/genai/tracing/integrations/listing/arize',
category: 'Tools',
},
{
id: 'goose',
name: 'Goose',
logoPath: '/images/logos/goose-logo.png',
link: '/genai/tracing/integrations/listing/goose',
category: 'No-Code',
},
// Gateways
{
id: 'mlflow-ai-gateway',
name: 'MLflow AI Gateway',
logoPath: '/images/logos/mlflow-logo.svg',
link: '/genai/tracing/integrations/listing/mlflow-ai-gateway',
category: 'Gateways',
},
{
id: 'databricks-ai-gateway',
name: 'Databricks',
logoPath: '/images/logos/databricks-logo.png',
link: '/genai/tracing/integrations/listing/databricks-ai-gateway',
category: 'Gateways',
},
{
id: 'litellm-proxy',
name: 'LiteLLM Proxy',
logoPath: '/images/logos/litellm-logo.png',
link: '/genai/tracing/integrations/listing/litellm-proxy',
category: 'Gateways',
},
{
id: 'vercel-ai-gateway',
name: 'Vercel AI Gateway',
logoPath: '/images/logos/vercel-logo.svg',
link: '/genai/tracing/integrations/listing/vercel-ai-gateway',
category: 'Gateways',
},
{
id: 'openrouter',
name: 'OpenRouter',
logoPath: '/images/logos/openrouter-logo.png',
link: '/genai/tracing/integrations/listing/openrouter',
category: 'Gateways',
},
{
id: 'portkey',
name: 'Portkey',
logoPath: '/images/logos/portkey-logo.png',
link: '/genai/tracing/integrations/listing/portkey',
category: 'Gateways',
},
{
id: 'helicone',
name: 'Helicone',
logoPath: '/images/logos/helicone-logo.png',
link: '/genai/tracing/integrations/listing/helicone',
category: 'Gateways',
},
{
id: 'kong',
name: 'Kong AI Gateway',
logoPath: '/images/logos/kong-logo.png',
link: '/genai/tracing/integrations/listing/kong',
category: 'Gateways',
},
{
id: 'pydantic-ai-gateway',
name: 'Pydantic AI Gateway',
logoPath: '/images/logos/pydantic-ai-logo-only.png',
link: '/genai/tracing/integrations/listing/pydantic-ai-gateway',
category: 'Gateways',
},
{
id: 'truefoundry',
name: 'TrueFoundry',
logoPath: '/images/logos/truefoundry-logo.png',
link: '/genai/tracing/integrations/listing/truefoundry',
category: 'Gateways',
},
{
id: 'langflow',
name: 'Langflow',
logoPath: '/images/logos/langflow.svg',
link: '/genai/tracing/integrations/listing/langflow',
category: 'No-Code',
},
{
id: 'open-webui',
name: 'Open WebUI',
logoPath: '/images/logos/open-webui-logo.png',
link: '/genai/tracing/integrations/listing/open-webui',
category: 'No-Code',
},
{
id: 'n8n',
name: 'n8n',
logoPath: '/images/logos/n8n-logo.png',
link: '/genai/tracing/integrations/listing/n8n',
category: 'No-Code',
},
];
const IntegrationSection: React.FC<{
title: string;
integrations: TracingIntegration[];
cardGroupProps?: TracingIntegrationsProps['cardGroupProps'];
}> = ({ integrations, cardGroupProps = {} }) => {
if (integrations.length === 0) return null;
return (
<div style={{ marginBottom: '2rem' }}>
<CardGroup isSmall={cardGroupProps.isSmall} cols={cardGroupProps.cols} noGap={cardGroupProps.noGap}>
{integrations.map((integration) => (
<SmallLogoCard key={integration.id} link={integration.link} title={integration.name}>
<span>
<img src={useBaseUrl(integration.logoPath)} alt={`${integration.name} Logo`} />
</span>
</SmallLogoCard>
))}
</CardGroup>
</div>
);
};
const getIntegrations = (predicate: (integration: TracingIntegration) => boolean) =>
TRACING_INTEGRATIONS.filter(predicate);
export const TracingIntegrationsSection: React.FC<TracingIntegrationsProps> = ({ category, cardGroupProps = {} }) => {
const targetCategories = category ? [category] : CATEGORY_ORDER;
const sections = targetCategories
.map((cat) => ({
title: cat,
integrations: getIntegrations((integration) => integration.category === cat),
}))
.filter(({ integrations }) => integrations.length > 0);
return (
<>
{sections.map(({ title, integrations }) => (
<IntegrationSection key={title} title={title} integrations={integrations} cardGroupProps={cardGroupProps} />
))}
</>
);
};
export const TracingIntegrations: React.FC<TracingIntegrationsProps> = ({ cardGroupProps = {}, category }) => (
<TracingIntegrationsSection category={category} cardGroupProps={cardGroupProps} />
);
export default TracingIntegrations;
+25
View File
@@ -0,0 +1,25 @@
type TreeViewItem = {
name: string;
items?: TreeViewItem[];
};
type TreeViewProps = {
items?: TreeViewItem[];
};
export function TreeView({ items }: TreeViewProps) {
if (!items || items.length === 0) {
return null;
}
return (
<ul style={{ fontSize: '1.25rem' }}>
{items.map((i) => (
<>
<li className="badge badge--info">{i.name}</li>
{i.items ? <TreeView items={i.items} /> : <br />}
</>
))}
</ul>
);
}
@@ -0,0 +1,52 @@
import React from 'react';
import { LucideIcon } from 'lucide-react';
import styles from './styles.module.css';
import ImageBox from '../ImageBox';
interface WorkflowStep {
title: string;
description: string | React.ReactNode;
icon?: LucideIcon;
}
interface WorkflowStepsProps {
steps: WorkflowStep[];
title?: string;
screenshot?: {
src: string;
alt: string;
width?: string;
};
width?: 'normal' | 'wide';
}
const WorkflowSteps: React.FC<WorkflowStepsProps> = ({ steps, title, screenshot, width = 'normal' }) => {
return (
<div className={styles.workflowContainer}>
{title && <h3 className={styles.workflowTitle}>{title}</h3>}
{screenshot && (
<div className={styles.screenshotContainer}>
<ImageBox src={screenshot.src} alt={screenshot.alt} width={screenshot.width || '90%'} />
</div>
)}
<div className={styles.stepsContainer} style={{ maxWidth: width === 'wide' ? '850px' : '700px' }}>
{steps.map((step, index) => (
<div key={index} className={styles.stepItem}>
<div className={styles.stepIndicator}>
<div className={styles.stepNumber}>
{step.icon ? <step.icon size={16} /> : <span className={styles.stepNumberText}>{index + 1}</span>}
</div>
{index < steps.length - 1 && <div className={styles.stepConnector} />}
</div>
<div className={styles.stepContent}>
<h4 className={styles.stepTitle}>{step.title}</h4>
<p className={styles.stepDescription}>{step.description}</p>
</div>
</div>
))}
</div>
</div>
);
};
export default WorkflowSteps;
@@ -0,0 +1,148 @@
.workflowContainer {
margin: 2rem 0;
}
.workflowTitle {
font-size: 1.25rem;
margin-bottom: 2rem;
color: var(--ifm-color-emphasis-900);
text-align: center;
font-weight: 600;
}
/* Layout containers */
.workflowStandalone {
display: block;
}
.stepsContainer {
display: flex;
flex-direction: column;
gap: 0;
margin: 0 auto;
}
.screenshotContainer {
display: flex;
justify-content: center;
align-items: flex-start;
padding: 1.5rem;
background: var(--ifm-background-surface-color);
border-radius: 12px;
border: 1px solid var(--ifm-color-emphasis-200);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
margin-bottom: 1rem;
max-width: 900px;
margin-left: auto;
margin-right: auto;
}
.screenshotImage {
max-width: 100%;
height: auto;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.stepItem {
display: flex;
align-items: flex-start;
gap: 1rem;
position: relative;
}
.stepIndicator {
display: flex;
flex-direction: column;
align-items: center;
flex-shrink: 0;
position: relative;
}
.stepNumber {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: 50%;
background: #0194e2;
color: white;
font-weight: 600;
font-size: 0.9rem;
margin-bottom: 0.5rem;
z-index: 2;
position: relative;
}
.stepNumberText {
line-height: 1;
}
.stepConnector {
width: 2px;
height: 3rem;
background: #b3e0f7;
margin-top: -0.5rem;
margin-bottom: 0.5rem;
}
.stepContent {
flex: 1;
padding-bottom: 2rem;
}
.stepTitle {
font-size: 1.1rem;
font-weight: 600;
margin: 0 0 0.5rem 0;
color: var(--ifm-color-emphasis-900);
line-height: 1.3;
}
.stepDescription {
color: var(--ifm-color-emphasis-700);
font-size: 0.95rem;
line-height: 1.6;
margin: 0;
}
/* Remove connector from last step */
.stepItem:last-child .stepConnector {
display: none;
}
.stepItem:last-child .stepContent {
padding-bottom: 0;
}
/* Responsive design */
@media (max-width: 768px) {
.screenshotContainer {
padding: 1rem;
margin-bottom: 1rem;
}
.stepsContainer {
max-width: 100%;
padding: 0 1rem;
}
.stepContent {
padding-bottom: 1.5rem;
}
.stepNumber {
width: 28px;
height: 28px;
font-size: 0.85rem;
}
.stepTitle {
font-size: 1rem;
}
.stepDescription {
font-size: 0.9rem;
}
}
+1
View File
@@ -0,0 +1 @@
export const Version = '3.14.1.dev0';
+34
View File
@@ -0,0 +1,34 @@
import ImageBox from "@site/src/components/ImageBox";
import { Rocket } from "lucide-react";
<details>
<summary>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
<Rocket />
<b>Try the MLflow LLMs and Agents Demo</b>
</div>
<br/>
The quickest way to learn about MLflow for LLMs and AI Agents is to try the demo. **Click to launch the demo ↓**
</summary>
#### **Public Demo**
Visit **[demo.mlflow.org](https://demo.mlflow.org/#/experiments/1)** to explore a publicly hosted MLflow instance pre-loaded with sample data.
#### **Starting from UI**
To start the demo, click on the "Start Demo" button on the top page of the MLflow UI.
<ImageBox src="/images/llms/demo/demo.png" alt="MLflow GenAI Demo UI" />
#### **Starting from CLI**
Alternatively, you can start the demo from the command line using the `mlflow demo` command. This option does
not require you to have a running MLflow server.
```bash
uvx mlflow demo
```
</details>
+29
View File
@@ -0,0 +1,29 @@
## Prerequisites for running the examples
1. Install MLflow and required packages
```bash
pip install --upgrade mlflow
```
2. Create an MLflow experiment by following the [setup your environment quickstart](/genai/getting-started/connect-environment/).
3. (Optional, if using OpenAI models) Use the native OpenAI SDK to connect to OpenAI-hosted models. Select a model from the [available OpenAI models](https://platform.openai.com/docs/models).
```python
import mlflow
import os
import openai
# Ensure your OPENAI_API_KEY is set in your environment
# os.environ["OPENAI_API_KEY"] = "<YOUR_API_KEY>" # Uncomment and set if not globally configured
# Enable auto-tracing for OpenAI
mlflow.openai.autolog()
# Create an OpenAI client
client = openai.OpenAI()
# Select an LLM
model_name = "gpt-4o-mini"
```
+24
View File
@@ -0,0 +1,24 @@
import { CardGroup, SmallLogoCard } from '@site/src/components/Card';
MLflow can be used in a variety of environments, including your local environment, on-premises clusters, cloud platforms, and managed services. Being an open-source platform, MLflow is **vendor-neutral**; whether you're building AI agents, LLM applications, or ML models, you have access to MLflow's core capabilities — tracing, evaluation, experiment tracking, deployment, and more.
<CardGroup cols={5}>
<SmallLogoCard link="https://docs.databricks.com/aws/en/mlflow3/genai/">
<span>![Databricks Logo](/images/logos/databricks-logo.png)</span>
</SmallLogoCard>
<SmallLogoCard link="https://aws.amazon.com/sagemaker-ai/experiments/">
<span>![Amazon SageMaker Logo](/images/logos/amazon-sagemaker-logo.png)</span>
</SmallLogoCard>
<SmallLogoCard link="https://learn.microsoft.com/en-us/azure/machine-learning/concept-mlflow?view=azureml-api-2">
<span>![Azure Machine Learning Logo](/images/logos/azure-ml-logo.png)</span>
</SmallLogoCard>
<SmallLogoCard link="https://docs.redhat.com/en/documentation/red_hat_openshift_ai_self-managed/latest/html/working_with_mlflow/about-mlflow_mlflow">
<span>![Red Hat OpenShift AI Logo](/images/logos/rhoai-logo.png)</span>
</SmallLogoCard>
<SmallLogoCard link="https://nebius.com/services/managed-mlflow">
<span>![Nebius Logo](/images/logos/nebius-logo.png)</span>
</SmallLogoCard>
<SmallLogoCard link="/ml/tracking">
<span>![Kubernetes Logo](/images/logos/kubernetes-logo.png)</span>
</SmallLogoCard>
</CardGroup>
+5
View File
@@ -0,0 +1,5 @@
## Select the LLM that powers the judge
You can change the judge model by using the `model` argument in the judge definition. The model must be specified in the format `<provider>:/<model-name>`, where `<provider>` is the name of your model provider (e.g. `openai`, `anthropic`, `gemini`).
For a list of supported models, see [selecting judge models](/genai/eval-monitor/scorers/llm-judge/custom-judges/#selecting-judge-models).
+59
View File
@@ -0,0 +1,59 @@
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import TabsWrapper from '@site/src/components/TabsWrapper';
<TabsWrapper>
<Tabs>
<TabItem value="uv" label="Local (uv)" default>
Install the Python package manager [uv](https://docs.astral.sh/uv/getting-started/installation/)
(that will also install [`uvx` command](https://docs.astral.sh/uv/guides/tools/) to invoke Python tools without installing them).
Start a MLflow server locally.
```shell
uvx mlflow server
```
:::info
See [Secure Installs](/self-hosting/security/secure-installs) to learn how to pin dependencies to known good versions using hash checking and upload-time filtering.
:::
</TabItem>
<TabItem value="local" label="Local (pip)">
**Python Environment**: Python 3.10+
Install the `mlflow` Python package via `pip` and start a MLflow server locally.
```shell
pip install --upgrade mlflow
mlflow server
```
:::info
See [Secure Installs](/self-hosting/security/secure-installs) to learn how to pin dependencies to known good versions using hash checking and upload-time filtering.
:::
</TabItem>
<TabItem value="docker" label="Local (docker)">
MLflow provides a Docker Compose file to start a local MLflow server with a PostgreSQL database and a MinIO server.
```shell
git clone --depth 1 --filter=blob:none --sparse https://github.com/mlflow/mlflow.git
cd mlflow
git sparse-checkout set docker-compose
cd docker-compose
cp .env.dev.example .env
docker compose up -d
```
Refer to the [instruction](https://github.com/mlflow/mlflow/tree/master/docker-compose/README.md) for more details (e.g., overriding the default environment variables).
</TabItem>
</Tabs>
</TabsWrapper>
+34
View File
@@ -0,0 +1,34 @@
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import TabsWrapper from '@site/src/components/TabsWrapper';
<TabsWrapper>
<Tabs>
<TabItem value="local" label="Local (pip)" default>
If you have a local Python environment >= 3.10, you can start the MLflow server locally using the `mlflow` CLI command.
```bash
mlflow server
```
</TabItem>
<TabItem value="docker" label="Local (docker)">
MLflow also provides a Docker Compose file to start a local MLflow server with a postgres database and a minio server.
```bash
git clone --depth 1 --filter=blob:none --sparse https://github.com/mlflow/mlflow.git
cd mlflow
git sparse-checkout set docker-compose
cd docker-compose
cp .env.dev.example .env
docker compose up -d
```
Refer to the [instruction](https://github.com/mlflow/mlflow/tree/master/docker-compose/README.md) for more details, e.g., overriding the default environment variables.
</TabItem>
</Tabs>
</TabsWrapper>
+985
View File
@@ -0,0 +1,985 @@
@import 'tailwindcss';
/**
* MLflow Documentation Styling
* Organized by section and functionality
*
* Any CSS included here will be global. The classic template
* bundles Infima by default. Infima is a CSS framework designed to
* work well for content-centric websites.
*/
:root {
--color-brand-red: #eb1700;
}
/* --------------------------------
* 0. Override tailwinds reset of li and ul elements styling
* --------------------------------*/
.markdown ul,
.theme-doc-markdown ul,
article ul {
list-style-type: disc;
padding-left: 1.5rem;
margin-bottom: 1rem;
}
.markdown ol,
.theme-doc-markdown ol,
article ol {
list-style-type: decimal;
padding-left: 1.5rem;
margin-bottom: 1rem;
}
.markdown li,
.theme-doc-markdown li,
article li {
margin-bottom: 0.25rem;
}
/* Nested lists */
.markdown ul ul,
.theme-doc-markdown ul ul,
article ul ul {
list-style-type: circle;
margin-bottom: 0.5rem;
}
.markdown ul ul ul,
.theme-doc-markdown ul ul ul,
article ul ul ul {
list-style-type: square;
}
/* --------------------------------
* 1. Font Imports and Typography
* -------------------------------- */
/* Import DM Sans for body text and DM Mono for code */
@import url('https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&display=swap');
:root[data-theme='light'] {
--background-color: var(--background-color-light);
}
/* Apply fonts */
:root {
--background-color-light: white;
--background-color-dark: rgb(14, 20, 22);
--background-color: var(--background-color-dark);
background-color: var(--background-color);
/* Font family variables */
--ifm-font-family-base:
'DM Sans', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans',
'Helvetica Neue', sans-serif;
--ifm-font-family-monospace:
'DM Mono', SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
--ifm-navbar-background-color: var(--background-color);
/* Font weight variables - per designer specs */
--font-weight-light: 300;
--font-weight-regular: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
/* Typography scale based on designer specs */
/* the font variables are overwritten by Docusaurus */
/* https://github.com/facebook/docusaurus/issues/6934 */
--font-size-h1: 3rem;
--font-size-h2: 2rem;
--font-size-h3: 1.5rem;
--font-size-h4: 1.3rem;
--font-size-h5: 1.2rem;
--font-size-body-l: 18px;
--font-size-body-m: 16px;
--font-size-body-s: 14px;
--font-size-label-l: 14px;
--font-size-label-s: 12px;
/* Line heights per designer specs */
--line-height-h1: 100%;
--line-height-h2: 120%;
--line-height-h3: 120%;
--line-height-h4: 120%;
--line-height-h5: 120%;
--line-height-body: 140%;
--line-height-label: 120%;
/* Letter spacing per designer specs */
--letter-spacing-h1: -3%;
--letter-spacing-h2: -1%;
--letter-spacing-h3: -1%;
--letter-spacing-h4: 0;
--letter-spacing-h5: 0;
--letter-spacing-body: 0;
--letter-spacing-label: 8%;
/* Map to Docusaurus variables */
--ifm-font-size-base: var(--font-size-body-s);
--ifm-line-height-base: 1.4;
--ifm-h1-font-size: var(--font-size-h1);
--ifm-h2-font-size: var(--font-size-h2);
--ifm-h3-font-size: var(--font-size-h3);
--ifm-h4-font-size: var(--font-size-h4);
--ifm-h5-font-size: var(--font-size-h5);
/* Common layout variables */
--card-border-radius: 4px;
--card-shadow1: rgba(50, 50, 93, 0.05);
--card-shadow2: rgba(50, 50, 93, 0.08);
--card-shadow3: rgba(0, 0, 0, 0.05);
--card-hover-shadow:
0 0 0 1px var(--card-shadow1), 0 0 14px 5px var(--card-shadow2), 0 0 10px 3px var(--card-shadow3);
--padding-xs: 4px;
--padding-sm: 8px;
--padding-md: 16px;
--padding-lg: 24px;
--ifm-code-font-size: 95%;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
/* Color system */
/* Light mode colors */
--ifm-border-color: #6b7280;
--ifm-color-emphasis-hover: #000000;
--ifm-link-color: #0194e2;
/* Site-wide shadow colors */
--ifm-shadow-light: rgba(0, 0, 0, 0.1);
--ifm-shadow-medium: rgba(0, 0, 0, 0.15);
--ifm-shadow-heavy: rgba(0, 0, 0, 0.25);
--ifm-shadow-tile-hover: 0 10px 30px var(--ifm-shadow-light);
/* Dynamic Coloring based on theme selection */
--ifm-color-primary: #0194e2;
--ifm-color-primary-dark: #0086cf;
--ifm-color-primary-darker: #0072b0;
--ifm-color-primary-darkest: #02659c;
--ifm-color-primary-light: #43c9ed;
--ifm-color-primary-lighter: #4accf0;
--ifm-color-primary-lightest: #54cef0;
/* ML - Blue theme */
--ml-color-primary: #0194e2;
/* GenAI - Red theme */
--genai-color-primary: #eb1700;
--genai-color-primary-dark: #d21400;
--genai-color-primary-darker: #b91100;
--genai-color-primary-darkest: #9f0f00;
--genai-color-primary-light: #ff3a23;
--genai-color-primary-lighter: #ff6c59;
--genai-color-primary-lightest: #ff9d8f;
/* Secondary colors */
--ifm-color-secondary: #85552e;
--ifm-color-secondary-dark: #784c29;
--ifm-color-secondary-darker: #714827;
--ifm-color-secondary-darkest: #5d3b20;
--ifm-color-secondary-light: #925d33;
--ifm-color-secondary-lighter: #996235;
--ifm-color-secondary-lightest: #ad6e3c;
/* Danger colors */
--ifm-color-danger: #852e5e;
--ifm-color-danger-dark: #782955;
--ifm-color-danger-darker: #712750;
--ifm-color-danger-darkest: #5d2042;
--ifm-color-danger-light: #923368;
--ifm-color-danger-lighter: #99356c;
--ifm-color-danger-lightest: #ad3c7b;
/* Info colors */
--ifm-color-info: #2e8581;
--ifm-color-info-dark: #297873;
--ifm-color-info-darker: #27716d;
--ifm-color-info-darkest: #205d5a;
--ifm-color-info-light: #33928c;
--ifm-color-info-lighter: #359994;
--ifm-color-info-lightest: #3cada7;
/* Footer colors */
--ifm-footer-background-color: #303846;
--ifm-footer-color: #ebedf0;
--ifm-footer-link-color: #ebedf0;
}
body {
font-family: var(--ifm-font-family-base);
font-size: var(--font-size-body-s);
font-weight: var(--font-weight-regular);
line-height: var(--line-height-body);
letter-spacing: var(--letter-spacing-body);
}
/* Unified link styling for both Classic ML and GenAI modes */
a {
color: var(--ifm-link-color);
text-decoration: none;
transition: color 0.2s ease;
}
a:hover {
color: var(--ifm-color-emphasis-hover);
text-decoration: none;
}
/* Exception for sidebar links which have their own styling */
.menu__link {
color: var(--ifm-border-color);
}
.menu__link:hover {
color: var(--ifm-color-emphasis-hover);
}
h1 {
font-family: var(--ifm-font-family-base);
font-weight: var(--font-weight-light);
line-height: var(--line-height-h1);
letter-spacing: -0.03em; /* -3% */
margin-top: 1.5rem;
margin-bottom: 1rem;
}
h2 {
font-family: var(--ifm-font-family-base);
font-size: var(--ifm-h2-font-size) !important;
font-weight: var(--font-weight-light);
line-height: var(--line-height-h2);
letter-spacing: -0.01em; /* -1% */
margin-top: 1.5rem;
margin-bottom: 1rem;
}
h3 {
font-family: var(--ifm-font-family-base);
font-size: var(--ifm-h3-font-size);
font-weight: var(--font-weight-regular);
line-height: var(--line-height-h3);
letter-spacing: -0.01em; /* -1% */
margin-top: 1.5rem;
margin-bottom: 1rem;
}
h4 {
font-family: var(--ifm-font-family-base);
font-size: var(--ifm-h4-font-size);
font-weight: var(--font-weight-regular);
line-height: var(--line-height-h4);
letter-spacing: 0; /* 0% */
margin-top: 1.5rem;
margin-bottom: 1rem;
}
h5 {
font-family: var(--ifm-font-family-base);
font-size: var(--ifm-h5-font-size);
font-weight: var(--font-weight-medium);
line-height: var(--line-height-h5);
letter-spacing: 0; /* 0% */
margin-top: 1.5rem;
margin-bottom: 1rem;
}
/* Body text classes */
.body-large {
font-size: var(--font-size-body-l);
font-weight: var(--font-weight-regular);
line-height: var(--line-height-body);
letter-spacing: var(--letter-spacing-body);
}
.body-medium {
font-size: var(--font-size-body-m);
font-weight: var(--font-weight-regular);
line-height: var(--line-height-body);
letter-spacing: var(--letter-spacing-body);
}
.body-small {
font-size: var(--font-size-body-s);
font-weight: var(--font-weight-regular);
line-height: var(--line-height-body);
letter-spacing: var(--letter-spacing-body);
}
/* Label classes */
.label-large {
font-size: var(--font-size-label-l);
font-weight: var(--font-weight-medium);
line-height: var(--line-height-label);
letter-spacing: 0.08em; /* 8% */
text-transform: uppercase;
}
.label-small {
font-size: var(--font-size-label-s);
font-weight: var(--font-weight-semibold);
line-height: var(--line-height-label);
letter-spacing: 0.08em; /* 8% */
text-transform: uppercase;
}
code,
pre,
kbd {
font-family: var(--ifm-font-family-monospace);
}
/* Dark mode overrides */
[data-theme='dark'] {
--ifm-color-primary: #0194e2;
--ifm-color-primary-dark: #0086cf;
--ifm-color-primary-darker: #0072b0;
--ifm-color-primary-darkest: #02659c;
--ifm-color-primary-light: #43c9ed;
--ifm-color-primary-lighter: #4accf0;
--ifm-color-primary-lightest: #54cef0;
/* Dark mode colors */
--ifm-border-color: #9ca3af;
--ifm-color-emphasis-hover: #ffffff;
--ifm-link-color: #43c9ed;
/* Dark mode shadow colors */
--ifm-shadow-light: rgba(255, 255, 255, 0.1);
--ifm-shadow-medium: rgba(255, 255, 255, 0.15);
--ifm-shadow-heavy: rgba(255, 255, 255, 0.25);
--ifm-shadow-tile-hover: 0 10px 30px var(--ifm-shadow-light);
/* Inverted shadows */
--card-shadow1: rgba(162, 162, 162, 0.5);
--card-shadow2: rgba(162, 162, 162, 0.5);
--card-shadow3: rgba(255, 255, 255, 0.3);
/* Secondary colors */
--ifm-color-secondary: #c2a025;
--ifm-color-secondary-dark: #af9021;
--ifm-color-secondary-darker: #a5881f;
--ifm-color-secondary-darkest: #88701a;
--ifm-color-secondary-light: #d5b029;
--ifm-color-secondary-lighter: #d8b432;
--ifm-color-secondary-lightest: #ddbf4f;
/* Danger colors */
--ifm-color-danger: #c22547;
--ifm-color-danger-dark: #af2140;
--ifm-color-danger-darker: #a51f3c;
--ifm-color-danger-darkest: #881a32;
--ifm-color-danger-light: #d5294e;
--ifm-color-danger-lighter: #d83256;
--ifm-color-danger-lightest: #dd4f6d;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
/* Info colors */
--ifm-color-info: #2595c2;
--ifm-color-info-dark: #2187af;
--ifm-color-info-darker: #1f7fa5;
--ifm-color-info-darkest: #1a6988;
--ifm-color-info-light: #29a4d5;
--ifm-color-info-lighter: #32a9d8;
--ifm-color-info-lightest: #4fb4dd;
}
/* --------------------------------
* 2. Theme Switching
* -------------------------------- */
/* GenAI theme activation */
html[data-genai-theme='true'] {
--ifm-color-primary: var(--genai-color-primary);
--ifm-color-primary-dark: var(--genai-color-primary-dark);
--ifm-color-primary-darker: var(--genai-color-primary-darker);
--ifm-color-primary-darkest: var(--genai-color-primary-darkest);
--ifm-color-primary-light: var(--genai-color-primary-light);
--ifm-color-primary-lighter: var(--genai-color-primary-lighter);
--ifm-color-primary-lightest: var(--genai-color-primary-lightest);
}
/* --------------------------------
* 3. Navigation Bar Styling
* -------------------------------- */
/* Base styling for all navbar links */
.navbar__link {
color: var(--ifm-border-color);
position: relative;
transition: color 0.2s ease;
}
/* Hover state for navbar links */
.navbar__link:not(.navbar__link--active):hover {
color: var(--ifm-color-emphasis-hover);
background-color: transparent;
}
.navbar__item:hover::before {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 2px;
background-color: var(--ifm-border-color);
opacity: 0.5;
}
.github-link {
display: flex;
align-items: center;
flex-wrap: nowrap;
}
/* On narrow viewports the navbar collapses into the hamburger menu, which
already contains a "GitHub" entry. The persistent top-bar GitHub link
overlaps the search button at these widths and gets clipped to "GitHu",
so hide it here and rely on the hamburger menu entry instead. */
@media (max-width: 996px) {
.navbar__items--right .github-link {
display: none;
}
}
/* --------------------------------
* 4. Sidebar Styling
* -------------------------------- */
/* Sidebar background color and spacing */
.theme-doc-sidebar-container {
background-color: transparent;
border-right: none !important;
padding-top: 1rem;
margin-right: 2rem;
}
/* Common sidebar styles for both themes */
.menu__list {
padding-left: 0.5rem;
}
.sidebar-top-level-category > .menu__link,
.sidebar-top-level-category > .menu__list-item-collapsible > .menu__link {
font-size: var(--font-size-body-s);
font-weight: var(--font-weight-medium);
padding-top: 0.5rem;
padding-bottom: 0.5rem;
padding-left: 1.5rem;
}
/* Style Overview doc link to match bold HTML section headers (e.g., "Getting Started")
Used in docs/sidebarsGenAI.ts */
.sidebar-overview.menu__list-item {
padding: var(--ifm-menu-link-padding-vertical) var(--ifm-menu-link-padding-horizontal) !important;
}
.sidebar-overview.menu__list-item > .menu__link {
font-weight: bolder !important;
padding-left: 0 !important;
}
.menu__list-item:not(.sidebar-top-level-category):not(.sidebar-overview) > .menu__link,
.menu__list-item:not(.sidebar-top-level-category):not(.sidebar-overview) > .menu__list-item-collapsible > .menu__link {
font-size: var(--font-size-body-s);
padding-left: 1.5rem;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
}
/* Regular menu links */
.menu__link {
color: var(--ifm-border-color);
transition: color 0.2s ease;
}
/* Active link styling for light and dark modes */
.menu__link--active {
position: relative;
}
/* Light mode */
html[data-theme='light'] .menu__link--active {
color: #000000; /* Pure black text */
background-color: transparent; /* No background color */
font-weight: var(--font-weight-medium);
}
/* Dark mode */
html[data-theme='dark'] .menu__link--active {
color: #ffffff; /* Pure white text */
background-color: transparent; /* No background color */
font-weight: var(--font-weight-medium);
}
/* Make the collapse button smaller */
.menu__caret:before,
.menu__link--sublist-caret:after {
background: var(--ifm-menu-link-sublist-icon) 50% / 1.5rem 1.5rem;
}
/* Hover states */
.menu__link:hover:not(.menu__link--active) {
color: var(--ifm-color-emphasis-hover); /* Use hover variable */
background-color: transparent;
}
/* External link icon in sidebar - make existing icon smaller */
.menu__link--external svg,
.menu__link[href^='http'] svg,
.menu__link[href^='https'] svg,
.menu a[target='_blank'] svg,
.menu a[rel*='noopener'] svg {
width: 10px;
height: 10px;
vertical-align: middle;
margin-left: 0.2em;
}
/* --------------------------------
* 5. Content Navigation
* -------------------------------- */
/* Align breadcrumb items */
.breadcrumbs {
display: flex;
}
.breadcrumbs__item {
display: inline-flex;
align-items: center;
}
/* Breadcrumbs - make uniform with the link color scheme */
.breadcrumbs__link {
color: var(--ifm-border-color);
}
.breadcrumbs__link:hover {
color: var(--ifm-color-emphasis-hover);
}
/* Active breadcrumb - consistent with active sidebar styling */
.breadcrumbs__link--active {
color: var(--ifm-color-emphasis-hover); /* Black in light mode, White in dark mode */
}
/* Breadcrumb separator */
.breadcrumbs__item--active .breadcrumbs__link {
background: none;
color: var(--ifm-color-emphasis-hover);
}
.pagination-nav {
gap: 0;
}
/* Make pagination consistent with the link colors */
.pagination-nav__link {
color: var(--ifm-border-color);
border-radius: 0;
padding-block: 2rem;
position: relative;
}
.pagination-nav__link:hover {
background-color: var(--ifm-background-color);
border-color: var(--ifm-color-emphasis-300);
}
.pagination-nav__link .pagination-nav__sublabel {
font-size: var(--font-size-base);
color: var(--ifm-border-color);
padding-top: 0.5rem;
}
.pagination-nav__link .pagination-nav__label {
font-size: var(--font-size-h5);
color: var(--ifm-color-emphasis-hover);
}
.pagination-nav__link--next {
padding-right: 3rem;
}
.pagination-nav__link--prev {
padding-left: 3rem;
}
/* Pagination arrows now use the same color scheme instead of theme colors */
.pagination-nav__link--next .pagination-nav__label::after {
content: '';
}
.pagination-nav__link--prev .pagination-nav__label::before {
content: '';
}
.pagination-nav__link--next::after {
content: '>';
color: var(--ifm-border-color);
font-size: 2rem;
position: absolute;
right: 1rem;
top: 33%;
}
.pagination-nav__link--prev::before {
content: '<';
color: var(--ifm-border-color);
font-size: 2rem;
position: absolute;
left: 1rem;
top: 33%;
}
/* Hover state for pagination arrows */
.pagination-nav__link:hover .pagination-nav__label::after,
.pagination-nav__link:hover .pagination-nav__label::before {
color: var(--ifm-color-emphasis-hover);
}
/* --------------------------------
* 6. Layout Utilities
* -------------------------------- */
.flex-column {
display: flex;
flex-direction: column;
}
.flex-row {
display: flex;
flex-direction: row;
}
.flex-item {
box-sizing: border-box;
flex: 1;
}
.padding-md {
padding: var(--padding-md);
}
.center-div {
margin-inline: auto;
}
/* --------------------------------
* 6.1. Modern Landing Page Components
* -------------------------------- */
/* Hero Section */
.hero-section {
max-width: 1200px;
margin: 0 auto;
padding: 4rem 2rem;
}
.hero-content {
display: flex;
align-items: center;
gap: 4rem;
min-height: 400px;
}
.hero-text {
flex: 1;
max-width: 600px;
}
.hero-text h1 {
font-size: 3.5rem;
font-weight: 300;
line-height: 1.1;
margin-bottom: 1.5rem;
color: var(--ifm-color-emphasis-hover);
}
.hero-text p {
font-size: 1.25rem;
line-height: 1.6;
color: var(--ifm-border-color);
margin-bottom: 2.5rem;
}
.hero-image {
flex: 1;
max-width: 500px;
display: flex;
justify-content: center;
align-items: center;
}
.hero-image img {
width: 100%;
height: auto;
border-radius: 12px;
box-shadow: var(--ifm-shadow-tile-hover);
}
/* Tiles Grid */
.tiles-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 2rem;
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
/* Responsive Design */
@media (max-width: 996px) {
.hero-content {
flex-direction: column;
text-align: center;
gap: 2rem;
}
.hero-text h1 {
font-size: 2.5rem;
}
.tiles-grid {
grid-template-columns: repeat(2, 1fr);
gap: 1.5rem;
}
.why-content {
flex-direction: column;
gap: 2rem;
}
.value-prop-badge {
justify-content: center;
}
.feature-highlights {
grid-template-columns: 1fr;
gap: 1.5rem;
}
}
@media (max-width: 768px) {
.hero-section {
padding: 2rem 1rem;
}
.hero-text h1 {
font-size: 2rem;
}
.tiles-grid {
grid-template-columns: 1fr;
gap: 1rem;
padding: 1rem;
}
.tile-card {
padding: 1.5rem;
}
}
/* --------------------------------
* 7. Image Styling
* -------------------------------- */
/* Most of our images are PNGs with transparent
backgrounds so they look bad in dark mode */
main img {
background-color: white;
}
/* We need to this class to limit image size using max-height. The class
must be applied to the wrapping div along with the desired max-height */
.max-height-img-container {
display: flex;
}
.max-height-img-container > * {
display: flex;
justify-content: center;
}
.max-height-img-container img {
max-height: 100%;
width: auto;
}
/* --------------------------------
* 7.1. Video Styling
* -------------------------------- */
video {
border-radius: 12px;
border: 1px solid var(--ifm-color-gray-300);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
}
/* --------------------------------
* 8. Accessibility
* -------------------------------- */
/* This class is to be used in non-heading elements that contain a hash link reference.
In headlines Docusaurus adds this for us. */
.anchor-with-sticky-navbar {
scroll-margin-top: calc(var(--ifm-navbar-height) + 0.5rem);
}
/* --------------------------------
* 9. Landing Page Specific Styles
* -------------------------------- */
.homepage .megaHeading {
font-family: var(--ifm-font-family-base);
font-size: var(--font-size-h1);
font-weight: var(--font-weight-semibold);
line-height: var(--line-height-h1);
letter-spacing: -0.03em; /* -3% */
}
/* --------------------------------
* 10. Code Blocks & Syntax Highlighting
* -------------------------------- */
/* Code blocks */
.codeBlockContainer {
margin-bottom: 1.5rem;
}
.theme-code-block {
max-width: 100%;
min-width: 0;
}
/* Inline code */
:not(pre) > code {
border-radius: 4px;
padding: 0.2em 0.4em;
font-size: 85%;
}
/* --------------------------------
* 11. Table Styles
* -------------------------------- */
table {
width: 100%;
margin-bottom: 1.5rem;
display: table;
border-collapse: separate;
border-spacing: 0;
}
th,
td {
padding: var(--padding-sm) var(--padding-md);
border: 1px solid var(--ifm-border-color);
}
th {
font-weight: var(--font-weight-semibold);
text-align: left;
}
/* --------------------------------
* 12. Tabs Component
* -------------------------------- */
.tabs {
margin-bottom: 1.5rem;
}
.tabs__item {
padding: var(--padding-sm) var(--padding-md);
border-radius: 0;
color: var(--ifm-border-color);
}
.tabs__item:hover {
color: var(--ifm-color-emphasis-hover);
background-color: transparent;
}
.tabs__item--active {
color: var(--ifm-color-emphasis-hover);
border-bottom: 2px solid var(--ifm-color-emphasis-hover);
}
[role='tabpanel'] {
padding-left: 1.5rem;
margin-top: 0.5rem;
margin-left: 0.5rem;
}
/* --------------------------------
* 13. Admonitions (Callouts)
* -------------------------------- */
.admonition {
margin-bottom: 1.5rem;
border-radius: 8px;
border-left: 8px solid;
box-shadow: var(--card-hover-shadow);
}
.admonition-heading {
font-weight: var(--font-weight-medium);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.admonition-content {
color: inherit;
}
/* Add underline to admonition links for better visibility */
.theme-admonition a {
text-decoration: underline;
}
.theme-admonition a:hover {
color: var(--ifm-color-emphasis-hover);
}
/* --------------------------------
* 14. Footer
* -------------------------------- */
.footer {
padding: 3rem 1rem;
}
.footer__title {
font-weight: var(--font-weight-medium);
margin-bottom: 1rem;
}
.footer__link-item {
color: var(--ifm-footer-link-color);
}
.footer__link-item:hover {
color: #ffffff;
text-decoration: none;
}
.footer__copyright {
margin-top: 2rem;
font-size: var(--font-size-body-s);
}
/* --------------------------------
* 15. Documentation body
* -------------------------------- */
[class*='docItemCol'] {
padding-inline: 2rem;
}
.col--3 {
padding-left: 0;
}
.theme-layout-main {
margin-left: auto;
margin-right: auto;
}
+15
View File
@@ -0,0 +1,15 @@
export function onRouteDidUpdate({ location }) {
const { pathname } = location;
document.body.classList.remove('mlflow-ml-section', 'mlflow-genai-section');
if (pathname.startsWith('/genai')) {
document.documentElement.setAttribute('data-genai-theme', 'true');
document.body.classList.add('mlflow-genai-section');
} else if (pathname.startsWith('/ml')) {
document.documentElement.removeAttribute('data-genai-theme');
document.body.classList.add('mlflow-ml-section');
} else {
document.documentElement.removeAttribute('data-genai-theme');
}
}
+243
View File
@@ -0,0 +1,243 @@
/* Main layout container */
.homeContainer {
display: flex;
flex-direction: column;
align-items: center;
max-width: 1600px;
margin: 0 auto;
padding: 8rem 1rem 4rem;
}
/* --------------------------------
* 1. Two Column Layout
* -------------------------------- */
.contentGrid {
display: flex;
flex-direction: row;
gap: 8rem;
width: 100%;
margin-top: 2rem;
align-items: flex-start;
min-height: calc(100vh - 300px);
padding-inline: 2rem;
}
.textColumn {
flex: 1;
max-width: 650px;
}
.cardsColumn {
flex: 1;
max-width: 650px;
display: flex;
flex-direction: column;
gap: 2rem;
}
/* --------------------------------
* 2. Typography
* -------------------------------- */
.megaHeading {
font-size: 4rem;
line-height: 1.1;
font-weight: 200;
margin-bottom: 1.5rem;
letter-spacing: -0.02em;
color: var(--ifm-heading-color);
}
.introText {
font-size: 1.125rem;
line-height: 1.6;
color: var(--ifm-color-emphasis-800);
margin-bottom: 2rem;
}
/* --------------------------------
* 3. Glossy Card Component
* -------------------------------- */
.glossyCard {
border-radius: 12px;
overflow: hidden;
background: var(--ifm-card-background-color);
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
border: 1px solid var(--ifm-color-emphasis-600);
margin-bottom: 2rem;
}
[data-theme='dark'] .glossyCard {
border: 1px solid var(--ifm-color-emphasis-300);
}
.glossyCardblue:hover {
transform: translateY(-5px);
box-shadow: 0 12px 40px rgba(1, 148, 226, 0.2);
border-color: rgba(1, 148, 226, 0.3);
background-color: rgba(1, 148, 226, 0.03);
}
.glossyCardred:hover {
transform: translateY(-5px);
box-shadow: 0 12px 40px rgba(235, 23, 0, 0.2);
border-color: rgba(235, 23, 0, 0.3);
background-color: rgba(235, 23, 0, 0.03);
}
.cardContent {
padding: 1.5rem;
}
.cardHeader {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1rem;
}
.colorBlock {
width: 12px;
height: 12px;
border-radius: 4px;
flex-shrink: 0;
}
.colorBlockblue {
background-color: var(--ifm-color-primary);
}
.colorBlockred {
background-color: var(--genai-color-primary);
}
.cardTitle {
font-size: 1.5rem;
font-weight: 600;
margin: 0;
}
[data-theme='light'] .cardTitle {
color: var(--ifm-color-emphasis-900);
}
[data-theme='dark'] .cardTitle {
color: rgba(255, 255, 255, 0.9);
}
.cardDescription {
font-size: 1rem;
line-height: 1.6;
color: var(--ifm-color-emphasis-700);
margin: 0.75rem 0 1.5rem;
}
/* --------------------------------
* 4. Card Actions & Buttons
* -------------------------------- */
.cardActions {
margin-top: 1.5rem;
display: flex;
flex-direction: row;
gap: 3rem;
}
.cardButton {
display: inline-flex;
align-items: center;
padding: 0.75rem 1.25rem;
border-radius: 8px;
font-weight: 600;
background-color: #ffffff;
text-decoration: none;
cursor: pointer;
transition: all 0.2s ease;
}
/* Light mode border color */
[data-theme='light'] .cardButton {
border: 1px solid var(--ifm-border-color);
color: var(--ifm-color-emphasis-900);
}
/* Dark mode styling */
[data-theme='dark'] .cardButton {
border: 1px solid var(--ifm-color-emphasis-300);
background-color: var(--ifm-color-emphasis-100);
color: var(--ifm-color-emphasis-800);
}
.arrowIcon {
margin-left: 0.5rem;
transition: transform 0.2s ease;
}
/* Arrow hover styling */
.cardButton:hover .arrowIcon {
transform: translateX(3px);
}
/* Button hover styling */
.cardButton:hover {
background-color: rgba(0, 0, 0, 0.03);
}
[data-theme='dark'] .cardButton:hover {
background-color: var(--ifm-color-emphasis-200);
color: rgba(0, 0, 0, 0.9);
border-color: var(--ifm-color-emphasis-400);
}
/* --------------------------------
* 5. Dark Mode Adjustments
* -------------------------------- */
[data-theme='dark'] .glossyCardblue:hover {
background-color: rgba(1, 148, 226, 0.1);
}
[data-theme='dark'] .glossyCardred:hover {
background-color: rgba(235, 23, 0, 0.1);
}
/* --------------------------------
* 6. Responsive Adjustments
* -------------------------------- */
@media (max-width: 1600px) {
.contentGrid {
gap: 4rem;
}
}
@media (max-width: 996px) {
.contentGrid {
flex-direction: column;
}
.textColumn,
.cardsColumn {
max-width: 100%;
}
.homeContainer {
padding: 2rem 0 4rem;
}
}
@media (max-width: 768px) {
.megaHeading {
font-size: 2.5rem;
}
.introText {
font-size: 1rem;
}
.cardActions {
flex-direction: column;
gap: 1rem;
}
.homeContainer {
padding: 0 0 2rem;
}
}
+127
View File
@@ -0,0 +1,127 @@
import React, { useState, useEffect } from 'react';
import { Redirect } from '@docusaurus/router';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import Layout from '@theme/Layout';
import clsx from 'clsx';
import styles from './index.module.css';
import useBaseUrl from '@docusaurus/useBaseUrl';
import Link from '@docusaurus/Link';
interface PathSelectorProps {
title: string;
description: string;
color: 'blue' | 'red';
buttons: {
text: string;
link: string;
}[];
}
function PathSelector({ title, description, color, buttons }: PathSelectorProps): JSX.Element {
return (
<div className={clsx(styles.glossyCard, styles[`glossyCard${color}`])}>
<div className={styles.cardContent}>
<div className={styles.cardHeader}>
<div className={clsx(styles.colorBlock, styles[`colorBlock${color}`])}></div>
<h2 className={styles.cardTitle}>{title}</h2>
</div>
<p className={styles.cardDescription}>{description}</p>
<div className={styles.cardActions}>
{buttons.map((button, index) => (
<a
key={index}
href={button.link}
className={styles.cardButton}
onClick={(e) => {
e.stopPropagation();
}}
>
{button.text} <span className={styles.arrowIcon}></span>
</a>
))}
</div>
</div>
</div>
);
}
export default function Home(): JSX.Element {
const { siteConfig } = useDocusaurusContext();
const [redirect, setRedirect] = useState<string | null>(null);
useEffect(() => {
document.body.classList.add('homepage');
return () => {
document.body.classList.remove('homepage');
};
}, []);
if (redirect) {
return <Redirect to={redirect} />;
}
return (
<Layout
title="MLflow Documentation"
description="Official MLflow documentation for LLM tracing, agent evaluation, prompt management, AI governance, experiment tracking, model registry, and beyond."
>
<main className={styles.homeContainer}>
<div className={styles.contentGrid}>
<div className={styles.textColumn}>
<h1 className={styles.megaHeading}>MLflow Documentation</h1>
<p className={styles.introText}>
Welcome to the MLflow Documentation. Our documentation is organized into two sections to help you find
exactly what you need. Choose{' '}
<Link to="/genai/">
<strong>LLMs & Agents</strong>
</Link>{' '}
for agent and LLM application development, tracing, and evaluation tools, or select{' '}
<Link to="/ml/">
<strong>Machine Learning</strong>
</Link>{' '}
for traditional ML workflows.
</p>
</div>
<div className={styles.cardsColumn}>
<PathSelector
title="LLMs & Agents"
description="Explore tools for LLM and agent observability, prompt management, foundation model deployment,
and evaluation frameworks. Learn how to track, evaluate, and optimize your
LLM applications and agent workflows with MLflow."
color="red"
buttons={[
{
text: 'Open Source',
link: useBaseUrl('/genai/'),
},
{
text: 'MLflow on Databricks',
link: 'https://docs.databricks.com/aws/en/mlflow3/genai/',
},
]}
/>
<PathSelector
title="Machine Learning"
description="Access comprehensive guides for experiment tracking, model packaging, registry management,
and deployment. Get started with MLflow's core functionality for traditional machine
learning workflows, hyperparameter tuning, and model lifecycle management."
color="blue"
buttons={[
{
text: 'Open Source',
link: useBaseUrl('/ml/'),
},
{
text: 'MLflow on Databricks',
link: 'https://docs.databricks.com/aws/en/mlflow/',
},
]}
/>
</div>
</div>
</main>
</Layout>
);
}
+13
View File
@@ -0,0 +1,13 @@
function tailwindPlugin(context, options) {
return {
name: "tailwind-plugin",
configurePostCss(postcssOptions) {
postcssOptions.plugins = [
require("@tailwindcss/postcss"),
];
return postcssOptions;
},
};
}
module.exports = tailwindPlugin;
@@ -0,0 +1,20 @@
import React, { type ReactNode } from 'react';
import clsx from 'clsx';
import Buttons from '@theme/CodeBlock/Buttons';
import styles from './styles.module.css';
export interface ContentHeaderProps {
language?: string;
}
export default function ContentHeader({ language }: ContentHeaderProps): ReactNode {
return (
<div
className={clsx(styles.codeBlockHeader)}
aria-label={`Code block header for ${language} code with copy and toggle buttons`}
>
<span className={styles.languageLabel}>{language}</span>
<Buttons />
</div>
);
}
@@ -0,0 +1,16 @@
.codeBlockHeader {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.25rem 0.5rem;
background: var(--prism-background-color);
position: sticky;
top: var(--ifm-navbar-height, 3.125rem); /* Stick below the Docusaurus navbar; fallback for safety */
}
.languageLabel {
font-size: 0.75rem;
font-weight: 600;
color: var(--ifm-color-emphasis-600);
padding: 0.3125rem;
}
+28
View File
@@ -0,0 +1,28 @@
import React, { type ReactNode } from 'react';
import clsx from 'clsx';
import { useCodeBlockContext } from '@docusaurus/theme-common/internal';
import Container from '@theme/CodeBlock/Container';
import Title from '@theme/CodeBlock/Title';
import Content from '@theme/CodeBlock/Content';
import type { Props } from '@theme/CodeBlock/Layout';
import ContentHeader from '../ContentHeader';
import styles from './styles.module.css';
export default function CodeBlockLayout({ className }: Props): ReactNode {
const { metadata } = useCodeBlockContext();
const language = metadata.language || 'text'; // Use 'text' as a fallback when metadata.language is undefined.
return (
<Container as="div" className={clsx(className, metadata.className)}>
{metadata.title && (
<div className={styles.codeBlockTitle}>
<Title>{metadata.title}</Title>
</div>
)}
<div className={styles.codeBlockContent}>
<ContentHeader language={language} />
<Content />
</div>
</Container>
);
}
@@ -0,0 +1,20 @@
.codeBlockContent {
position: relative;
/* rtl:ignore */
direction: ltr;
border-radius: inherit;
}
.codeBlockTitle {
border-bottom: 1px solid var(--ifm-color-emphasis-300);
font-size: var(--ifm-code-font-size);
font-weight: 500;
padding: 0.75rem var(--ifm-pre-padding);
border-top-left-radius: inherit;
border-top-right-radius: inherit;
}
.codeBlockTitle + .codeBlockContent .codeBlock {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
+25
View File
@@ -0,0 +1,25 @@
import { useLocation } from '@docusaurus/router';
import useBaseUrl from '@docusaurus/useBaseUrl';
import { Footer } from '@site/src/components/Footer/Footer';
import React from 'react';
function getFooterVariant(pathname: string) {
const genAI = useBaseUrl('/genai');
const classicalML = useBaseUrl('/ml');
if (pathname.startsWith(genAI)) {
return 'red';
} else if (pathname.startsWith(classicalML)) {
return 'blue';
} else {
return 'colorful';
}
}
function FooterWrapper() {
const location = useLocation();
const variant = getFooterVariant(location.pathname);
return <Footer variant={variant} />;
}
export default React.memo(FooterWrapper);
@@ -0,0 +1,9 @@
import ComponentTypes from '@theme-original/NavbarItem/ComponentTypes';
import DocsDropdown from '@site/src/components/NavbarItems/DocsDropdown';
import VersionSelector from '@site/src/components/NavbarItems/VersionSelector';
export default {
...ComponentTypes,
'custom-docsDropdown': DocsDropdown,
'custom-versionSelector': VersionSelector,
};