Enterprise Edition features for Lightdash Common providing advanced functionality including AI Agents, embedding with interactivity controls, SCIM user provisioning, and service account authentication.
Enterprise features controlled by feature flags.
enum CommercialFeatureFlags {
Embedding = 'embedding',
Scim = 'scim-token-management',
AiCopilot = 'ai-copilot',
ServiceAccounts = 'service-accounts',
OrganizationWarehouseCredentials = 'organization-warehouse-credentials',
AgentReasoning = 'agent-reasoning',
}Comprehensive AI assistant functionality with conversation threads, tool calls, artifact management, admin controls, and evaluation capabilities.
/**
* AI Agent configuration with instructions, integrations, and access controls
*/
interface AiAgent {
uuid: string;
projectUuid: string;
organizationUuid: string;
name: string;
description: string | null;
imageUrl: string | null;
tags: string[] | null;
integrations: Array<{
type: 'slack';
channelId: string;
}>;
createdAt: Date;
updatedAt: Date;
instruction: string | null; // Custom instructions (max 8192 characters)
provider: string;
model: string;
groupAccess: string[];
userAccess: string[];
spaceAccess: string[];
enableDataAccess: boolean;
enableSelfImprovement: boolean;
enableReasoning: boolean;
version: number;
}
/**
* Compact agent summary for listing
*/
type AiAgentSummary = Pick<
AiAgent,
| 'uuid'
| 'name'
| 'description'
| 'integrations'
| 'tags'
| 'projectUuid'
| 'organizationUuid'
| 'createdAt'
| 'updatedAt'
| 'instruction'
| 'imageUrl'
| 'groupAccess'
| 'userAccess'
| 'spaceAccess'
| 'enableDataAccess'
| 'enableSelfImprovement'
| 'enableReasoning'
| 'version'
>;
/**
* Conversation thread containing messages
*/
interface AiAgentThread<TUser extends AiAgentUser = AiAgentUser> {
uuid: string;
agentUuid: string;
createdAt: string;
createdFrom: string;
title: string | null;
titleGeneratedAt: string | null;
firstMessage: {
uuid: string;
message: string;
};
user: TUser;
messages: AiAgentMessage<TUser>[];
}
/**
* User message in conversation
*/
interface AiAgentMessageUser<TUser extends AiAgentUser = AiAgentUser> {
role: 'user';
uuid: string;
threadUuid: string;
message: string;
createdAt: string;
user: TUser;
}
/**
* Assistant response with tool calls and results
*/
interface AiAgentMessageAssistant {
role: 'assistant';
status: 'idle' | 'pending' | 'error';
uuid: string;
threadUuid: string;
message: string | null;
createdAt: string;
humanScore: number | null;
humanFeedback?: string | null;
toolCalls: AiAgentToolCall[];
toolResults: AiAgentToolResult[];
reasoning: AiAgentReasoning[];
savedQueryUuid: string | null;
artifacts: AiAgentMessageAssistantArtifact[] | null;
referencedArtifacts: AiAgentMessageAssistantArtifact[] | null;
modelConfig: {
modelName: string;
modelProvider: string;
reasoning?: boolean;
} | null;
}
/**
* Tool invocation by AI agent
*/
interface AiAgentToolCall {
uuid: string;
promptUuid: string;
toolCallId: string;
createdAt: Date;
toolName: string;
toolArgs: object;
}
/**
* Result from tool execution
*/
type AiAgentToolResult = {
uuid: string;
promptUuid: string;
result: string;
createdAt: Date;
toolCallId: string;
} & (
| {
toolName: 'proposeChange';
metadata: ToolProposeChangeOutput['metadata'];
}
| {
toolName: Exclude<ToolName, 'proposeChange'>;
metadata: AgentToolOutput['metadata'];
}
);
/**
* Agent reasoning step (when enableReasoning is true)
*/
interface AiAgentReasoning {
uuid: string;
promptUuid: string;
reasoningId: string;
text: string;
createdAt: Date;
}
/**
* Saved artifact (chart or dashboard) created by agent
*/
interface AiArtifact {
artifactUuid: string;
threadUuid: string;
promptUuid: string | null;
artifactType: 'chart' | 'dashboard';
savedQueryUuid: string | null;
savedDashboardUuid: string | null;
createdAt: Date;
versionNumber: number;
versionUuid: string;
title: string | null;
description: string | null;
chartConfig:
| ToolTableVizArgs
| ToolTimeSeriesArgs
| ToolVerticalBarArgs
| ToolRunQueryArgs
| null;
dashboardConfig: ToolDashboardArgs | null;
versionCreatedAt: Date;
verifiedByUserUuid: string | null;
verifiedAt: Date | null;
}
/**
* User-verified artifact for reuse
*/
interface AiAgentVerifiedArtifact {
artifactUuid: string;
versionUuid: string;
artifactType: 'chart' | 'dashboard';
title: string | null;
description: string | null;
verifiedAt: Date;
verifiedBy: {
userUuid: string;
firstName: string;
lastName: string;
};
referenceCount: number;
threadUuid: string;
promptUuid: string | null;
}
/**
* Agent evaluation test suite
*/
interface AiAgentEvaluation {
evalUuid: string;
agentUuid: string;
title: string;
description: string | null;
createdAt: Date;
updatedAt: Date;
prompts: AiAgentEvaluationPrompt[];
}
/**
* Evaluation run results
*/
interface AiAgentEvaluationRun {
runUuid: string;
evalUuid: string;
status: 'pending' | 'running' | 'completed' | 'failed';
completedAt: Date | null;
createdAt: Date;
passedAssessments: number;
failedAssessments: number;
results: AiAgentEvaluationRunResult[];
}
/**
* Agent model configuration options
*/
interface AiModelOption {
name: string;
displayName: string;
description: string;
provider: string;
default: boolean;
supportsReasoning: boolean;
}Admin-level types for managing AI agents across the organization, viewing conversations, tracking feedback, and configuring organization-wide AI settings.
/**
* Filters for AI agent admin dashboard
*/
type AiAgentAdminFilters = {
projectUuids?: string[];
agentUuids?: string[];
userUuids?: string[];
createdFrom?: 'slack' | 'web_app';
humanScore?: number; // Feedback score: -1 (downvote), 0 (neutral), 1 (upvote)
dateFrom?: string; // ISO date string
dateTo?: string; // ISO date string
search?: string; // Search by thread title
};
/**
* Sort field options for admin conversations
*/
type AiAgentAdminSortField = 'createdAt' | 'title';
/**
* Sort configuration for admin views
*/
type AiAgentAdminSort = {
field: AiAgentAdminSortField;
direction: 'asc' | 'desc';
};
/**
* Aggregated feedback statistics for a thread
*/
type AiAgentAdminFeedbackSummary = {
upvotes: number;
downvotes: number;
neutral: number;
total: number;
};
/**
* Extended thread summary with admin-level information
*/
type AiAgentAdminThreadSummary = {
uuid: string;
createdAt: string;
createdFrom: 'slack' | 'web_app';
title: string | null;
user: AiAgentUser & {
slackUserId: string | null;
email: string | null;
};
agent: {
uuid: string;
name: string;
imageUrl: string | null;
};
project: {
uuid: string;
name: string;
};
feedbackSummary: AiAgentAdminFeedbackSummary;
promptCount: number;
slackChannelId: string | null;
slackThreadTs: string | null;
};
/**
* Collection of admin thread summaries
*/
type AiAgentAdminConversationsSummary = {
threads: AiAgentAdminThreadSummary[];
};
/**
* API response for admin conversations endpoint
*/
type ApiAiAgentAdminConversationsResponse = ApiSuccess<
KnexPaginatedData<AiAgentAdminConversationsSummary>
>;
/**
* Computed/derived AI settings for organization
*/
type ComputedAiOrganizationSettings = {
isCopilotEnabled: boolean;
isTrial: boolean;
};
/**
* AI feature settings at organization level
*/
type AiOrganizationSettings = {
organizationUuid: string;
aiAgentsVisible: boolean;
};
/**
* Create AI organization settings request
*/
type CreateAiOrganizationSettings = AiOrganizationSettings;
/**
* Update AI organization settings request
*/
type UpdateAiOrganizationSettings = Omit<
AiOrganizationSettings,
'organizationUuid' | 'isTrial' | 'isCopilotEnabled'
>;
/**
* API response for organization settings
*/
type ApiAiOrganizationSettingsResponse = ApiSuccess<
AiOrganizationSettings & ComputedAiOrganizationSettings
>;
/**
* API response for updating organization settings
*/
type ApiUpdateAiOrganizationSettingsResponse = ApiSuccess<AiOrganizationSettings>;Example:
import {
type AiAgentAdminFilters,
type AiAgentAdminSort,
type AiAgentAdminThreadSummary,
} from '@lightdash/common';
// Filter admin conversations by project and feedback
const filters: AiAgentAdminFilters = {
projectUuids: ['proj-123'],
humanScore: 1, // Only show upvoted conversations
dateFrom: '2024-01-01',
dateTo: '2024-12-31',
createdFrom: 'slack',
};
// Sort by most recent
const sort: AiAgentAdminSort = {
field: 'createdAt',
direction: 'desc',
};
// Thread summary includes agent, project, and feedback stats
const thread: AiAgentAdminThreadSummary = {
uuid: 'thread-456',
title: 'Revenue analysis request',
createdAt: '2024-06-15T10:30:00Z',
createdFrom: 'slack',
user: {
uuid: 'user-789',
name: 'John Doe',
email: 'john@example.com',
slackUserId: 'U123ABC',
},
agent: {
uuid: 'agent-001',
name: 'Analytics Assistant',
imageUrl: 'https://example.com/agent.png',
},
project: {
uuid: 'proj-123',
name: 'Sales Analytics',
},
feedbackSummary: {
upvotes: 5,
downvotes: 1,
neutral: 2,
total: 8,
},
promptCount: 12,
slackChannelId: 'C123DEF',
slackThreadTs: '1234567890.123456',
};Types for evaluating AI agent performance with test suites and automated assessments (human or LLM judge).
/**
* Type of assessment
*/
type AssessmentType = 'human' | 'llm';
/**
* Assessment record for evaluation run result
*/
type AiEvalRunResultAssessment = {
assessmentUuid: string;
runResultUuid: string;
assessmentType: AssessmentType;
passed: boolean;
reason: string | null;
assessedByUserUuid: string | null; // Set when assessmentType is 'human'
llmJudgeProvider: string | null; // Set when assessmentType is 'llm'
llmJudgeModel: string | null; // Set when assessmentType is 'llm'
assessedAt: Date;
createdAt: Date;
};
/**
* Create LLM judge assessment request
*/
type CreateLlmAssessment = {
runResultUuid: string;
passed: boolean;
reason: string | null;
llmJudgeProvider: string;
llmJudgeModel: string;
};Example:
import {
type AiEvalRunResultAssessment,
type CreateLlmAssessment,
type AssessmentType,
} from '@lightdash/common';
// Human assessment of agent response
const humanAssessment: AiEvalRunResultAssessment = {
assessmentUuid: 'assess-123',
runResultUuid: 'result-456',
assessmentType: 'human',
passed: true,
reason: 'Response was accurate and helpful',
assessedByUserUuid: 'user-789',
llmJudgeProvider: null,
llmJudgeModel: null,
assessedAt: new Date('2024-06-15T14:30:00Z'),
createdAt: new Date('2024-06-15T14:30:00Z'),
};
// LLM judge assessment of agent response
const llmAssessment: CreateLlmAssessment = {
runResultUuid: 'result-789',
passed: true,
reason: 'The response correctly identified the key metrics and provided accurate calculations.',
llmJudgeProvider: 'anthropic',
llmJudgeModel: 'claude-sonnet-4-5',
};
// Use in evaluation workflow
async function assessEvaluationResult(
resultUuid: string,
assessmentType: AssessmentType
) {
if (assessmentType === 'llm') {
// Create automated LLM assessment
const assessment: CreateLlmAssessment = {
runResultUuid: resultUuid,
passed: await llmJudge.evaluate(resultUuid),
reason: await llmJudge.getReason(resultUuid),
llmJudgeProvider: 'anthropic',
llmJudgeModel: 'claude-sonnet-4-5',
};
return assessment;
} else {
// Human assessment workflow
return await promptForHumanReview(resultUuid);
}
}Advanced embedding controls for dashboards and charts with filter, parameter, and export interactivity.
/**
* Filter interactivity level enum
*/
enum FilterInteractivityValues {
some = 'some', // Only specific filters are interactive
all = 'all', // All filters are interactive
none = 'none', // No filters are interactive
}
/**
* Dashboard filter interactivity configuration
*/
interface DashboardFilterInteractivityOptions {
enabled: boolean | FilterInteractivityValues;
allowedFilters?: string[] | null; // Required when enabled is 'some'
hidden?: boolean; // Whether filters should be hidden in UI
}
/**
* Parameter interactivity configuration
*/
interface ParameterInteractivityOptions {
enabled: boolean;
}
/**
* Dashboard interactivity options
*/
interface InteractivityOptions {
dashboardFiltersInteractivity?: DashboardFilterInteractivityOptions;
parameterInteractivity?: ParameterInteractivityOptions;
canExportCsv?: boolean;
canExportImages?: boolean;
canExportPagePdf?: boolean;
canDateZoom?: boolean;
canExplore?: boolean;
canViewUnderlyingData?: boolean;
}
/**
* Chart-specific interactivity options
*/
interface ChartInteractivityOptions {
scopes?: string[];
canExportCsv?: boolean;
canExportImages?: boolean;
canViewUnderlyingData?: boolean;
}
/**
* Embed JWT configuration for dashboard or chart
*/
interface EmbedJwt {
userAttributes?: Record<string, unknown>;
user?: {
externalId?: string;
email?: string;
};
content:
| {
type: 'dashboard';
projectUuid?: string;
dashboardUuid: string;
isPreview?: boolean;
} & InteractivityOptions
| {
type: 'dashboard';
projectUuid?: string;
dashboardSlug: string;
isPreview?: boolean;
} & InteractivityOptions
| {
type: 'chart';
projectUuid?: string;
contentId: string;
isPreview?: boolean;
} & ChartInteractivityOptions;
iat?: number;
exp: number;
}
/**
* Create embed JWT request
*/
interface CreateEmbedJwt {
content:
| EmbedJwtContentDashboardUuid
| EmbedJwtContentDashboardSlug
| EmbedJwtContentChart;
userAttributes?: { [key: string]: string };
user?: {
email?: string;
externalId?: string;
};
expiresIn?: string;
iat?: number;
exp?: number;
}
/**
* Check if filter interactivity is enabled
*/
function isFilterInteractivityEnabled(
filterInteractivityOptions?: DashboardFilterInteractivityOptions
): boolean;
/**
* Check if parameter interactivity is enabled
*/
function isParameterInteractivityEnabled(
parameterInteractivityOptions?: ParameterInteractivityOptions
): boolean;
/**
* Get filter interactivity value (handles backward compatibility)
*/
function getFilterInteractivityValue(
enabled: boolean | FilterInteractivityValues
): FilterInteractivityValues;
/**
* Type guard for dashboard UUID content
*/
function isDashboardUuidContent(
content: CreateEmbedJwt['content']
): content is EmbedJwtContentDashboardUuid;
/**
* Type guard for dashboard slug content
*/
function isDashboardSlugContent(
content: CreateEmbedJwt['content']
): content is EmbedJwtContentDashboardSlug;
/**
* Type guard for chart content
*/
function isChartContent(
content: CreateEmbedJwt['content']
): content is EmbedJwtContentChart;
/**
* Type guard for dashboard content (UUID or slug)
*/
function isDashboardContent(
content: CreateEmbedJwt['content'] | EmbedContent
): content is EmbedJwtContentDashboardUuid | EmbedJwtContentDashboardSlug;SCIM 2.0 user and group provisioning for automated user management.
/**
* SCIM schema type enum
*/
enum ScimSchemaType {
ERROR = 'urn:ietf:params:scim:api:messages:2.0:Error',
USER = 'urn:ietf:params:scim:schemas:core:2.0:User',
GROUP = 'urn:ietf:params:scim:schemas:core:2.0:Group',
ROLE = 'urn:ietf:params:scim:schemas:extension:2.0:Role',
LIST_RESPONSE = 'urn:ietf:params:scim:api:messages:2.0:ListResponse',
SCHEMA = 'urn:ietf:params:scim:schemas:core:2.0:Schema',
PATCH = 'urn:ietf:params:scim:api:messages:2.0:PatchOp',
LIGHTDASH_USER_EXTENSION = 'urn:lightdash:params:scim:schemas:extension:2.0:User',
SERVICE_PROVIDER_CONFIG = 'urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig',
RESOURCE_TYPE = 'urn:ietf:params:scim:schemas:core:2.0:ResourceType',
}
/**
* Base SCIM resource with metadata
*/
interface ScimResource {
schemas: string[];
id: string;
meta?: {
resourceType?: string;
created?: Date;
lastModified?: Date;
location?: string;
version?: string;
};
}
/**
* SCIM user resource
*/
interface ScimUser extends ScimResource {
schemas: string[];
userName: string;
name?: {
givenName?: string;
familyName?: string;
};
active?: boolean;
emails?: {
value: string;
primary?: boolean;
}[];
roles?: ScimUserRole[];
[ScimSchemaType.LIGHTDASH_USER_EXTENSION]?: LightdashScimExtension;
}
/**
* SCIM user role assignment
*/
interface ScimUserRole {
value: string;
display?: string;
type?: string;
primary?: boolean;
}
/**
* Lightdash SCIM extension for user roles
*/
interface LightdashScimExtension {
/**
* @deprecated - use ScimUser['roles'] instead
*/
role?: string;
}
/**
* SCIM group resource
*/
interface ScimGroup extends ScimResource {
schemas: string[];
id: string;
displayName: string;
members?: ScimGroupMember[];
meta: ScimResource['meta'] & {
resourceType: 'Group';
created: Date;
lastModified: Date;
location: string;
};
}
/**
* SCIM group member reference
*/
interface ScimGroupMember {
value: string;
display?: string;
}
/**
* SCIM role resource
*/
interface ScimRole extends ScimResource {
schemas: ScimSchemaType.ROLE[];
value: string;
display?: string;
type?: string; // Function level (e.g., Organization, Project)
supported: boolean;
meta: ScimResource['meta'] & {
resourceType: 'Role';
created?: Date;
lastModified?: Date;
location: string;
};
}
/**
* SCIM error response
*/
interface ScimErrorPayload {
scimType?:
| 'invalidFilter'
| 'tooMany'
| 'uniqueness'
| 'mutability'
| 'invalidSyntax'
| 'invalidPath'
| 'noTarget'
| 'invalidValue'
| 'invalidVers'
| 'sensitive';
schemas: ScimSchemaType.ERROR[];
detail: string;
status: string;
}
/**
* SCIM list response with pagination
*/
interface ScimListResponse<T extends ScimResource> {
schemas: ScimSchemaType.LIST_RESPONSE[];
totalResults: number;
itemsPerPage: number;
startIndex: number;
Resources: T[];
}
/**
* Service provider configuration
*/
interface ScimServiceProviderConfig {
schemas: ScimSchemaType.SERVICE_PROVIDER_CONFIG[];
documentationUri?: string;
patch: { supported: boolean };
bulk: {
supported: boolean;
maxOperations?: number;
maxPayloadSize?: number;
};
filter: {
supported: boolean;
maxResults?: number;
};
changePassword: { supported: boolean };
sort: { supported: boolean };
etag: { supported: boolean };
authenticationSchemes: {
type: string;
name: string;
description: string;
specUri?: string;
documentationUri?: string;
primary?: boolean;
}[];
}API authentication with scope-based access control for programmatic access.
/**
* Service account scope enum
*/
enum ServiceAccountScope {
SCIM_MANAGE = 'scim:manage',
ORG_ADMIN = 'org:admin',
ORG_EDIT = 'org:edit',
ORG_READ = 'org:read',
}
/**
* Service account with metadata and scopes
*/
interface ServiceAccount {
uuid: string;
createdByUserUuid: string | null;
organizationUuid: string;
createdAt: Date;
expiresAt: Date | null;
description: string;
lastUsedAt: Date | null;
rotatedAt: Date | null;
scopes: ServiceAccountScope[];
}
/**
* Service account with authentication token
*/
interface ServiceAccountWithToken extends ServiceAccount {
token: string;
}
/**
* Create service account request
*/
type ApiCreateServiceAccountRequest = Pick<
ServiceAccount,
'expiresAt' | 'description' | 'scopes'
>;
/**
* Create service account response with token
*/
interface ApiCreateServiceAccountResponse {
token: string;
expiresAt: Date;
}interface AiAgentUser {
uuid: string;
name: string;
}
type AiAgentMessage<TUser extends AiAgentUser = AiAgentUser> =
| AiAgentMessageUser<TUser>
| AiAgentMessageAssistant;
type AiAgentThreadSummary<TUser extends AiAgentUser = AiAgentUser> = Pick<
AiAgentThread<TUser>,
'uuid' | 'agentUuid' | 'createdAt' | 'createdFrom' | 'title' | 'titleGeneratedAt' | 'firstMessage' | 'user'
>;
type CreateEvaluationPrompt =
| { prompt: string; expectedResponse: string | null }
| { promptUuid: string; threadUuid: string; expectedResponse: string | null };
type SessionServiceAccount = {
organizationUuid: string;
};