diff --git a/apps/trigger/src/tasks/message-post-processing/helpers/data-transformers.test.ts b/apps/trigger/src/tasks/message-post-processing/helpers/data-transformers.test.ts index a78d06dc8..e84de4843 100644 --- a/apps/trigger/src/tasks/message-post-processing/helpers/data-transformers.test.ts +++ b/apps/trigger/src/tasks/message-post-processing/helpers/data-transformers.test.ts @@ -320,7 +320,13 @@ describe('data-transformers', () => { }); it('should handle empty conversation history', () => { - const result = buildWorkflowInput(baseMessageContext, [], basePreviousResults, baseDatasets, false); + const result = buildWorkflowInput( + baseMessageContext, + [], + basePreviousResults, + baseDatasets, + false + ); expect(result.conversationHistory).toBeUndefined(); }); }); diff --git a/apps/trigger/src/tasks/message-post-processing/helpers/slack-notifier.ts b/apps/trigger/src/tasks/message-post-processing/helpers/slack-notifier.ts index a2387e2b7..bf2daca4d 100644 --- a/apps/trigger/src/tasks/message-post-processing/helpers/slack-notifier.ts +++ b/apps/trigger/src/tasks/message-post-processing/helpers/slack-notifier.ts @@ -127,7 +127,7 @@ export async function getExistingSlackMessageForChat(chatId: string): Promise<{ * Send a Slack notification based on post-processing results */ export async function sendSlackNotification( - params: SlackNotificationParams, + params: SlackNotificationParams ): Promise { try { // Step 1: Check if organization has active Slack integration @@ -139,8 +139,8 @@ export async function sendSlackNotification( and( eq(slackIntegrations.organizationId, params.organizationId), eq(slackIntegrations.status, 'active'), - isNull(slackIntegrations.deletedAt), - ), + isNull(slackIntegrations.deletedAt) + ) ) .limit(1); @@ -189,7 +189,7 @@ export async function sendSlackNotification( const result = await sendSlackMessage( tokenSecret.secret, integration.defaultChannel.id, - slackMessage, + slackMessage ); if (result.success) { @@ -229,7 +229,7 @@ export async function sendSlackNotification( * Send a Slack reply notification to an existing thread */ export async function sendSlackReplyNotification( - params: SlackReplyNotificationParams, + params: SlackReplyNotificationParams ): Promise { try { // Step 1: Check if we should send a notification @@ -257,7 +257,7 @@ export async function sendSlackReplyNotification( tokenSecret.secret, params.channelId, slackMessage, - params.threadTs, + params.threadTs ); if (result.success) { @@ -372,7 +372,7 @@ function formatSlackReplyMessage(params: SlackReplyNotificationParams): SlackMes throw new Error( 'Invalid reply notification parameters: Missing required fields. ' + - 'Requires either formattedMessage, summaryTitle with summaryMessage, or toolCalled="flagChat" with message', + 'Requires either formattedMessage, summaryTitle with summaryMessage, or toolCalled="flagChat" with message' ); } @@ -456,7 +456,7 @@ function formatSlackMessage(params: SlackNotificationParams): SlackMessage { throw new Error( `Invalid notification parameters: Missing required fields. Requires either formattedMessage, summaryTitle with summaryMessage, or toolCalled="flagChat" with message. Received: formattedMessage=${!!params.formattedMessage}, summaryTitle=${!!params.summaryTitle}, summaryMessage=${!!params.summaryMessage}, toolCalled="${ params.toolCalled - }", message=${!!params.message}`, + }", message=${!!params.message}` ); } @@ -467,7 +467,7 @@ async function sendSlackMessage( accessToken: string, channelId: string, message: SlackMessage, - threadTs?: string, + threadTs?: string ): Promise<{ success: boolean; messageTs?: string; error?: string }> { try { const response = await fetch('https://slack.com/api/chat.postMessage', { @@ -540,8 +540,8 @@ export async function trackSlackNotification(params: { content: params.slackBlocks ? JSON.stringify({ blocks: params.slackBlocks }) : params.summaryTitle && params.summaryMessage - ? `${params.summaryTitle}\n\n${params.summaryMessage}` - : 'Notification sent', + ? `${params.summaryTitle}\n\n${params.summaryMessage}` + : 'Notification sent', senderInfo: { sentBy: 'buster-post-processing', userName: params.userName, diff --git a/apps/trigger/src/tasks/message-post-processing/message-post-processing.test.ts b/apps/trigger/src/tasks/message-post-processing/message-post-processing.test.ts index 497b16f89..32b2fd247 100644 --- a/apps/trigger/src/tasks/message-post-processing/message-post-processing.test.ts +++ b/apps/trigger/src/tasks/message-post-processing/message-post-processing.test.ts @@ -24,14 +24,16 @@ vi.mock('@buster/database', () => ({ getDb: vi.fn(), eq: vi.fn((a, b) => ({ type: 'eq', a, b })), messages: { id: 'messages.id' }, - getBraintrustMetadata: vi.fn(() => Promise.resolve({ - userName: 'John Doe', - userId: 'user-123', - organizationName: 'Test Org', - organizationId: 'org-123', - messageId: 'msg-12345', - chatId: 'chat-123', - })), + getBraintrustMetadata: vi.fn(() => + Promise.resolve({ + userName: 'John Doe', + userId: 'user-123', + organizationName: 'Test Org', + organizationId: 'org-123', + messageId: 'msg-12345', + chatId: 'chat-123', + }) + ), })); vi.mock('@buster/ai/workflows/post-processing-workflow', () => ({ diff --git a/apps/web/src/components/features/PermissionComponents/SelectDatasetInput.tsx b/apps/web/src/components/features/PermissionComponents/SelectDatasetInput.tsx index d1ea1fd3a..eefe109ad 100644 --- a/apps/web/src/components/features/PermissionComponents/SelectDatasetInput.tsx +++ b/apps/web/src/components/features/PermissionComponents/SelectDatasetInput.tsx @@ -1,6 +1,6 @@ import React, { useMemo, useState } from 'react'; import { useGetDatasets } from '@/api/buster_rest/datasets'; -import { Select, type SelectItem } from '@/components/ui/select/Select'; +import { Select, type SelectItem } from '@/components/ui/select/SelectOld'; import { SelectMultiple } from '@/components/ui/select/SelectMultiple'; import { useMemoizedFn } from '@/hooks'; diff --git a/apps/web/src/components/ui/select/Select.stories.tsx b/apps/web/src/components/ui/select/Select.stories.tsx index 3f441332b..4bb7288ef 100644 --- a/apps/web/src/components/ui/select/Select.stories.tsx +++ b/apps/web/src/components/ui/select/Select.stories.tsx @@ -1,127 +1,236 @@ import type { Meta, StoryObj } from '@storybook/react'; +import React from 'react'; import { fn } from '@storybook/test'; -import { Mailbox, MapSettings, User } from '../icons'; -import { Select } from './Select'; +import { Select, type SelectItem, type SelectProps } from './Select'; +import { User, Gear, PowerOff } from '@/components/ui/icons/NucleoIconOutlined'; -const meta: Meta = { - title: 'UI/Select/Select', +const meta = { + title: 'UI/select/Select2', component: Select, parameters: { layout: 'centered' }, - args: { - onChange: fn() + argTypes: { + search: { + control: { type: 'boolean' }, + description: 'Enable/disable search functionality' + }, + disabled: { + control: { type: 'boolean' }, + description: 'Disable the select' + }, + loading: { + control: { type: 'boolean' }, + description: 'Show loading state' + }, + showIndex: { + control: { type: 'boolean' }, + description: 'Show index numbers for items' + }, + placeholder: { + control: { type: 'text' }, + description: 'Placeholder text when no item is selected' + }, + onChange: { + action: 'onChange' + } }, - tags: ['autodocs'] -}; + decorators: [ + (Story) => ( +
+ +
+ ) + ] +} satisfies Meta; export default meta; -type Story = StoryObj; +type Story = StoryObj; -const basicItems = [ - { value: 'apples', label: 'Apples' }, - { value: 'bananas', label: 'Bananas' }, - { value: 'cherries', label: 'Cherries' } -]; - -const itemsWithIcons = [ - { value: 'profile', label: 'Profile', icon: }, - { value: 'settings', label: 'Settings', icon: }, - { value: 'messages', label: 'Messages', icon: } -]; - -const groupedItems = [ - { - label: 'Fruits', +// Basic select with simple string options +export const BasicSelect: Story = { + args: { + placeholder: 'Select a fruit', items: [ { value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }, - { value: 'orange', label: 'Orange' } - ] + { value: 'orange', label: 'Orange' }, + { value: 'grape', label: 'Grape' }, + { value: 'strawberry', label: 'Strawberry' }, + { value: 'watermelon', label: 'Watermelon' }, + { value: 'pineapple', label: 'Pineapple' }, + { value: 'mango', label: 'Mango' } + ] as SelectItem[], + onChange: fn() }, - { - label: 'Vegetables', - items: [ - { value: 'carrot', label: 'Carrot' }, - { value: 'broccoli', label: 'Broccoli' }, - { value: 'spinach', label: 'Spinach' } - ] - } -]; + render: function RenderBasicSelect(args) { + const [value, setValue] = React.useState(); -const itemsWithSecondaryLabel = [ - { value: 'user1', label: 'John Doe', secondaryLabel: 'Admin' }, - { value: 'user2', label: 'Jane Smith', secondaryLabel: 'Editor' }, - { value: 'user3', label: 'Bob Johnson', secondaryLabel: 'Viewer', disabled: true } -]; - -const itemsWithSomeDisabled = [ - { value: 'active1', label: 'Available Option 1' }, - { value: 'disabled1', label: 'Unavailable Option 1', disabled: true }, - { value: 'active2', label: 'Available Option 2' }, - { value: 'disabled2', label: 'Unavailable Option 2', disabled: true }, - { value: 'active3', label: 'Available Option 3' } -]; - -export const Basic: Story = { - args: { - items: basicItems, - placeholder: 'Select an option' + return ( + { + setValue(newValue as string); + args.onChange(newValue); + }} + /> + ); + } +}; + +// Select with search disabled +export const NoSearchSelect: Story = { + args: { + placeholder: 'Select a color', + search: false, + items: [ + { value: 'red', label: 'Red' }, + { value: 'green', label: 'Green' }, + { value: 'blue', label: 'Blue' }, + { value: 'yellow', label: 'Yellow' }, + { value: 'purple', label: 'Purple' }, + { value: 'orange', label: 'Orange' } + ] as SelectItem[], + onChange: fn() + }, + render: function RenderNoSearchSelect(args) { + const [value, setValue] = React.useState(); + + return ( + { + setValue(newValue as string); + args.onChange(newValue); + }} + /> + ); + } +}; + +// Select with clearable option +export const ClearableSelect: Story = { + args: { + placeholder: 'Select an option (clearable)', + clearable: true, + items: [ + { value: 'option1', label: 'Option 1' }, + { value: 'option2', label: 'Option 2' }, + { value: 'option3', label: 'Option 3' }, + { value: 'option4', label: 'Option 4' }, + { value: 'option5', label: 'Option 5' } + ] as SelectItem[], + onChange: fn() + }, + render: function RenderClearableSelect(args) { + const [value, setValue] = React.useState('option2'); + + return ( + +
+ {clearable && selectedItem && !isFocused && ( + + )} + {!open && ( +
+ +
+ )} +
+ + + { + e.preventDefault(); + inputRef.current?.focus(); + }}> + + {/* Hidden input that Command uses for keyboard navigation */} + + + ); -}; +} -SelectItemSelector.displayName = 'SelectItemSelector'; -const SelectItemSecondaryText: React.FC<{ children: React.ReactNode }> = ({ children }) => { - return {children}; -}; +export const Select = React.memo(SelectComponent) as typeof SelectComponent; diff --git a/apps/web/src/components/ui/select/Select2.stories.tsx b/apps/web/src/components/ui/select/Select2.stories.tsx deleted file mode 100644 index eb310b1d5..000000000 --- a/apps/web/src/components/ui/select/Select2.stories.tsx +++ /dev/null @@ -1,236 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; -import React from 'react'; -import { fn } from '@storybook/test'; -import { Select, type SelectItem, type SelectProps } from './Select2'; -import { User, Gear, PowerOff } from '@/components/ui/icons/NucleoIconOutlined'; - -const meta = { - title: 'UI/select/Select2', - component: Select, - parameters: { - layout: 'centered' - }, - argTypes: { - search: { - control: { type: 'boolean' }, - description: 'Enable/disable search functionality' - }, - disabled: { - control: { type: 'boolean' }, - description: 'Disable the select' - }, - loading: { - control: { type: 'boolean' }, - description: 'Show loading state' - }, - showIndex: { - control: { type: 'boolean' }, - description: 'Show index numbers for items' - }, - placeholder: { - control: { type: 'text' }, - description: 'Placeholder text when no item is selected' - }, - onChange: { - action: 'onChange' - } - }, - decorators: [ - (Story) => ( -
- -
- ) - ] -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -// Basic select with simple string options -export const BasicSelect: Story = { - args: { - placeholder: 'Select a fruit', - items: [ - { value: 'apple', label: 'Apple' }, - { value: 'banana', label: 'Banana' }, - { value: 'orange', label: 'Orange' }, - { value: 'grape', label: 'Grape' }, - { value: 'strawberry', label: 'Strawberry' }, - { value: 'watermelon', label: 'Watermelon' }, - { value: 'pineapple', label: 'Pineapple' }, - { value: 'mango', label: 'Mango' } - ] as SelectItem[], - onChange: fn() - }, - render: function RenderBasicSelect(args) { - const [value, setValue] = React.useState(); - - return ( - { - setValue(newValue as string); - args.onChange(newValue); - }} - /> - ); - } -}; - -// Select with search disabled -export const NoSearchSelect: Story = { - args: { - placeholder: 'Select a color', - search: false, - items: [ - { value: 'red', label: 'Red' }, - { value: 'green', label: 'Green' }, - { value: 'blue', label: 'Blue' }, - { value: 'yellow', label: 'Yellow' }, - { value: 'purple', label: 'Purple' }, - { value: 'orange', label: 'Orange' } - ] as SelectItem[], - onChange: fn() - }, - render: function RenderNoSearchSelect(args) { - const [value, setValue] = React.useState(); - - return ( - { - setValue(newValue as string); - args.onChange(newValue); - }} - /> - ); - } -}; - -// Select with clearable option -export const ClearableSelect: Story = { - args: { - placeholder: 'Select an option (clearable)', - clearable: true, - items: [ - { value: 'option1', label: 'Option 1' }, - { value: 'option2', label: 'Option 2' }, - { value: 'option3', label: 'Option 3' }, - { value: 'option4', label: 'Option 4' }, - { value: 'option5', label: 'Option 5' } - ] as SelectItem[], - onChange: fn() - }, - render: function RenderClearableSelect(args) { - const [value, setValue] = React.useState('option2'); - - return ( - -
- {clearable && selectedItem && !isFocused && ( - - )} - {!open && ( -
- -
- )} -
- - - { - e.preventDefault(); - inputRef.current?.focus(); - }}> - - {/* Hidden input that Command uses for keyboard navigation */} - - - - ); -} - -export const Select = React.memo(SelectComponent) as typeof SelectComponent; diff --git a/apps/web/src/components/ui/select/SelectMultiple.stories.tsx b/apps/web/src/components/ui/select/SelectMultiple.stories.tsx index 71c1f3a2b..29f1354be 100644 --- a/apps/web/src/components/ui/select/SelectMultiple.stories.tsx +++ b/apps/web/src/components/ui/select/SelectMultiple.stories.tsx @@ -4,7 +4,7 @@ import { faker } from '@faker-js/faker'; import type { Meta, StoryObj } from '@storybook/react'; import { fn } from '@storybook/test'; import { useMemo, useState } from 'react'; -import type { SelectItem } from './Select'; +import type { SelectItem } from './SelectOld'; import { SelectMultiple } from './SelectMultiple'; const meta = { diff --git a/apps/web/src/components/ui/select/SelectMultiple.tsx b/apps/web/src/components/ui/select/SelectMultiple.tsx index 0520f4a19..5f168945a 100644 --- a/apps/web/src/components/ui/select/SelectMultiple.tsx +++ b/apps/web/src/components/ui/select/SelectMultiple.tsx @@ -6,7 +6,7 @@ import { useMemoizedFn } from '@/hooks'; import { cn } from '@/lib/classMerge'; import { Dropdown, type DropdownItem, type DropdownProps } from '../dropdown/Dropdown'; import { InputTag } from '../inputs/InputTag'; -import type { SelectItem } from './Select'; +import type { SelectItem } from './SelectOld'; import { selectVariants } from './SelectBase'; import { CircleSpinnerLoader } from '../loaders'; diff --git a/apps/web/src/components/ui/select/SelectOld.stories.tsx b/apps/web/src/components/ui/select/SelectOld.stories.tsx new file mode 100644 index 000000000..68ebe68f5 --- /dev/null +++ b/apps/web/src/components/ui/select/SelectOld.stories.tsx @@ -0,0 +1,127 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { fn } from '@storybook/test'; +import { Mailbox, MapSettings, User } from '../icons'; +import { Select } from './SelectOld'; + +const meta: Meta = { + title: 'UI/Select/Select', + component: Select, + parameters: { + layout: 'centered' + }, + args: { + onChange: fn() + }, + tags: ['autodocs'] +}; + +export default meta; +type Story = StoryObj; + +const basicItems = [ + { value: 'apples', label: 'Apples' }, + { value: 'bananas', label: 'Bananas' }, + { value: 'cherries', label: 'Cherries' } +]; + +const itemsWithIcons = [ + { value: 'profile', label: 'Profile', icon: }, + { value: 'settings', label: 'Settings', icon: }, + { value: 'messages', label: 'Messages', icon: } +]; + +const groupedItems = [ + { + label: 'Fruits', + items: [ + { value: 'apple', label: 'Apple' }, + { value: 'banana', label: 'Banana' }, + { value: 'orange', label: 'Orange' } + ] + }, + { + label: 'Vegetables', + items: [ + { value: 'carrot', label: 'Carrot' }, + { value: 'broccoli', label: 'Broccoli' }, + { value: 'spinach', label: 'Spinach' } + ] + } +]; + +const itemsWithSecondaryLabel = [ + { value: 'user1', label: 'John Doe', secondaryLabel: 'Admin' }, + { value: 'user2', label: 'Jane Smith', secondaryLabel: 'Editor' }, + { value: 'user3', label: 'Bob Johnson', secondaryLabel: 'Viewer', disabled: true } +]; + +const itemsWithSomeDisabled = [ + { value: 'active1', label: 'Available Option 1' }, + { value: 'disabled1', label: 'Unavailable Option 1', disabled: true }, + { value: 'active2', label: 'Available Option 2' }, + { value: 'disabled2', label: 'Unavailable Option 2', disabled: true }, + { value: 'active3', label: 'Available Option 3' } +]; + +export const Basic: Story = { + args: { + items: basicItems, + placeholder: 'Select an option' + } +}; + +export const WithIcons: Story = { + args: { + items: itemsWithIcons, + placeholder: 'Select an option' + } +}; + +export const Grouped: Story = { + args: { + items: groupedItems, + placeholder: 'Select an option' + } +}; + +export const WithSecondaryLabels: Story = { + args: { + items: itemsWithSecondaryLabel, + placeholder: 'Select a user' + } +}; + +export const Disabled: Story = { + args: { + items: basicItems, + placeholder: 'Select an option', + disabled: true + } +}; + +export const WithShowIndex: Story = { + args: { + items: basicItems, + placeholder: 'Select an option', + showIndex: true + } +}; + +export const PartiallyDisabled: Story = { + args: { + items: itemsWithSomeDisabled, + placeholder: 'Select an available option' + } +}; + +export const WithLowCharacters: Story = { + args: { + items: [ + { + value: 'gyj', + label: 'gyj - GYJ' + } + ], + placeholder: 'Select an option' + } +}; diff --git a/apps/web/src/components/ui/select/SelectOld.tsx b/apps/web/src/components/ui/select/SelectOld.tsx new file mode 100644 index 000000000..3345246c1 --- /dev/null +++ b/apps/web/src/components/ui/select/SelectOld.tsx @@ -0,0 +1,129 @@ +import type React from 'react'; +import { useMemoizedFn } from '@/hooks'; +import { + Select as SelectBase, + SelectContent, + SelectGroup, + SelectItem as SelectItemComponent, + SelectLabel, + SelectTrigger, + SelectValue +} from './SelectBase'; +import { CircleSpinnerLoader } from '../loaders'; +import { cn } from '@/lib/classMerge'; + +interface SelectItemGroup { + label: string; + items: SelectItem[]; +} + +export interface SelectItem { + value: T; + label: string | React.ReactNode; //this will be used in the select item text + secondaryLabel?: string; + icon?: React.ReactNode; + searchLabel?: string; // Used for filtering + disabled?: boolean; +} + +export interface SelectProps { + items: SelectItem[] | SelectItemGroup[]; + disabled?: boolean; + onChange: (value: T) => void; + placeholder?: string; + value?: string | undefined; + onOpenChange?: (open: boolean) => void; + open?: boolean; + showIndex?: boolean; + className?: string; + defaultValue?: string; + dataTestId?: string; + loading?: boolean; +} + +export const Select = ({ + items, + showIndex, + disabled, + onChange, + placeholder, + value, + onOpenChange, + open, + loading = false, + className = '', + defaultValue, + dataTestId +}: SelectProps) => { + const onValueChange = useMemoizedFn((value: string) => { + onChange(value as T); + }); + return ( + + + + + + {items.map((item, index) => ( + + ))} + + + ); +}; +Select.displayName = 'Select'; + +const SelectItemSelector = ({ + item, + index, + showIndex +}: { + item: SelectItem | SelectItemGroup; + index: number; + showIndex?: boolean; +}) => { + const isGroup = typeof item === 'object' && 'items' in item; + + if (isGroup) { + const _item = item as SelectItemGroup; + return ( + + {_item.label} + {_item.items?.map((item) => ( + + ))} + + ); + } + + const { value, label, icon, secondaryLabel, disabled, ...rest } = item as SelectItem; + + return ( + {secondaryLabel} + }> + {label} + + ); +}; + +SelectItemSelector.displayName = 'SelectItemSelector'; +const SelectItemSecondaryText: React.FC<{ children: React.ReactNode }> = ({ children }) => { + return {children}; +}; diff --git a/apps/web/src/components/ui/select/index.ts b/apps/web/src/components/ui/select/index.ts index 7868ecbae..738e015b0 100644 --- a/apps/web/src/components/ui/select/index.ts +++ b/apps/web/src/components/ui/select/index.ts @@ -1 +1 @@ -export * from './Select'; +export * from './SelectOld'; diff --git a/packages/ai/src/steps/post-processing/combine-parallel-results-step.ts b/packages/ai/src/steps/post-processing/combine-parallel-results-step.ts index d66f95687..d47222b42 100644 --- a/packages/ai/src/steps/post-processing/combine-parallel-results-step.ts +++ b/packages/ai/src/steps/post-processing/combine-parallel-results-step.ts @@ -19,7 +19,9 @@ export const combineParallelResultsOutputSchema = z.object({ userId: z.string().describe('User ID for the current operation'), chatId: z.string().describe('Chat ID for the current operation'), isFollowUp: z.boolean().describe('Whether this is a follow-up message'), - isSlackFollowUp: z.boolean().describe('Whether this is a follow-up message for an existing Slack thread'), + isSlackFollowUp: z + .boolean() + .describe('Whether this is a follow-up message for an existing Slack thread'), previousMessages: z.array(z.string()).describe('Array of previous messages for context'), datasets: z.string().describe('Assembled YAML content of all available datasets for context'), diff --git a/packages/ai/src/steps/post-processing/flag-chat-step.ts b/packages/ai/src/steps/post-processing/flag-chat-step.ts index 4af76b893..fdcd927ba 100644 --- a/packages/ai/src/steps/post-processing/flag-chat-step.ts +++ b/packages/ai/src/steps/post-processing/flag-chat-step.ts @@ -15,7 +15,9 @@ const inputSchema = z.object({ userId: z.string().describe('User ID for the current operation'), chatId: z.string().describe('Chat ID for the current operation'), isFollowUp: z.boolean().describe('Whether this is a follow-up message'), - isSlackFollowUp: z.boolean().describe('Whether this is a follow-up message for an existing Slack thread'), + isSlackFollowUp: z + .boolean() + .describe('Whether this is a follow-up message for an existing Slack thread'), previousMessages: z.array(z.string()).describe('Array of previous messages for context'), datasets: z.string().describe('Assembled YAML content of all available datasets for context'), }); @@ -28,7 +30,9 @@ export const flagChatOutputSchema = z.object({ userId: z.string().describe('User ID for the current operation'), chatId: z.string().describe('Chat ID for the current operation'), isFollowUp: z.boolean().describe('Whether this is a follow-up message'), - isSlackFollowUp: z.boolean().describe('Whether this is a follow-up message for an existing Slack thread'), + isSlackFollowUp: z + .boolean() + .describe('Whether this is a follow-up message for an existing Slack thread'), previousMessages: z.array(z.string()).describe('Array of previous messages for context'), datasets: z.string().describe('Assembled YAML content of all available datasets for context'), diff --git a/packages/ai/src/steps/post-processing/identify-assumptions-step.ts b/packages/ai/src/steps/post-processing/identify-assumptions-step.ts index a9b7ff15f..7d2ec8f70 100644 --- a/packages/ai/src/steps/post-processing/identify-assumptions-step.ts +++ b/packages/ai/src/steps/post-processing/identify-assumptions-step.ts @@ -18,7 +18,9 @@ const inputSchema = z.object({ userId: z.string().describe('User ID for the current operation'), chatId: z.string().describe('Chat ID for the current operation'), isFollowUp: z.boolean().describe('Whether this is a follow-up message'), - isSlackFollowUp: z.boolean().describe('Whether this is a follow-up message for an existing Slack thread'), + isSlackFollowUp: z + .boolean() + .describe('Whether this is a follow-up message for an existing Slack thread'), previousMessages: z.array(z.string()).describe('Array of previous messages for context'), datasets: z.string().describe('Assembled YAML content of all available datasets for context'), }); @@ -31,7 +33,9 @@ export const identifyAssumptionsOutputSchema = z.object({ userId: z.string().describe('User ID for the current operation'), chatId: z.string().describe('Chat ID for the current operation'), isFollowUp: z.boolean().describe('Whether this is a follow-up message'), - isSlackFollowUp: z.boolean().describe('Whether this is a follow-up message for an existing Slack thread'), + isSlackFollowUp: z + .boolean() + .describe('Whether this is a follow-up message for an existing Slack thread'), previousMessages: z.array(z.string()).describe('Array of previous messages for context'), datasets: z.string().describe('Assembled YAML content of all available datasets for context'), diff --git a/packages/ai/src/steps/post-processing/schemas.ts b/packages/ai/src/steps/post-processing/schemas.ts index 143bf907f..c5d341fc7 100644 --- a/packages/ai/src/steps/post-processing/schemas.ts +++ b/packages/ai/src/steps/post-processing/schemas.ts @@ -9,7 +9,9 @@ export const postProcessingWorkflowInputSchema = z.object({ userId: z.string().describe('User ID for the current operation'), chatId: z.string().describe('Chat ID for the current operation'), isFollowUp: z.boolean().describe('Whether this is a follow-up message'), - isSlackFollowUp: z.boolean().describe('Whether this is a follow-up message for an existing Slack thread'), + isSlackFollowUp: z + .boolean() + .describe('Whether this is a follow-up message for an existing Slack thread'), previousMessages: z.array(z.string()).describe('Array of the previous post-processing messages'), datasets: z.string().describe('Assembled YAML content of all available datasets for context'), }); @@ -23,7 +25,9 @@ export const postProcessingWorkflowOutputSchema = z.object({ userId: z.string().describe('User ID for the current operation'), chatId: z.string().describe('Chat ID for the current operation'), isFollowUp: z.boolean().describe('Whether this is a follow-up message'), - isSlackFollowUp: z.boolean().describe('Whether this is a follow-up message for an existing Slack thread'), + isSlackFollowUp: z + .boolean() + .describe('Whether this is a follow-up message for an existing Slack thread'), previousMessages: z.array(z.string()).describe('Array of the previous post-processing messages'), datasets: z.string().describe('Assembled YAML content of all available datasets for context'), diff --git a/packages/ai/src/tools/visualization-tools/bar-line-axis-validator.ts b/packages/ai/src/tools/visualization-tools/bar-line-axis-validator.ts index 8bf318e1e..2adbc7bdd 100644 --- a/packages/ai/src/tools/visualization-tools/bar-line-axis-validator.ts +++ b/packages/ai/src/tools/visualization-tools/bar-line-axis-validator.ts @@ -30,7 +30,8 @@ export function validateAndAdjustBarLineAxes(metricYml: MetricYml): AxisValidati return { isValid: false, shouldSwapAxes: false, - error: 'Bar and line charts require at least one column for each axis. Please specify both X and Y axis columns.', + error: + 'Bar and line charts require at least one column for each axis. Please specify both X and Y axis columns.', }; } const xColumns = barAndLineAxis.x;