diff --git a/CHANGELOG.md b/CHANGELOG.md index c328e1ad..1e1c8bf8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## v3.1 +### Added +- Explore dropdown for asking questions against different explores + - only loads explores that exist in examples table on bigquery end + - extension frontend picks up explores without redeployment of app +- Dynamic explore samples loaded from samples table in BigQuery + - terraform update to deploy samples table + - bigquery loader script update to upload samples per explore + ## v3.0 ### Added diff --git a/README.md b/README.md index 6a4ecde8..6eeba97a 100644 --- a/README.md +++ b/README.md @@ -17,13 +17,14 @@ Additionally, the extension provides: - Flexible Deployment Options - Multi-turn - Insight Summarization + - Dynamic Explore Selection ### Technologies Used #### Frontend - [React](https://reactjs.org/) - [TypeScript](https://www.typescriptlang.org/) - [Webpack](https://webpack.js.org/). -- [Styled components](https://www.styled-components.com/docs) +- [Tailwind CSS](https://tailwindcss.com/) #### Looker - [Looker Extension SDK](https://github.com/looker-open-source/sdk-codegen/tree/main/packages/extension-sdk-react) @@ -37,11 +38,11 @@ Additionally, the extension provides: ## Setup -Getting started involves: +Getting started involves (*in this order*): -- Frontend Setup - setup Looker Extension Framework Applications by following [these instructions](./explore-assistant-extension/README.md). - Backend Setup - setup the GCP backend for communicating with the Vertex API [using these instructions.](./explore-assistant-backend/README.md) - Example generation - generate a list of examples and upload them to BigQuery [using these instructions.](./explore-assistant-examples/README.md) +- Frontend Setup - setup Looker Extension Framework Applications by following [these instructions](./explore-assistant-extension/README.md). The local cloud function backend and example generation require some python packages. It is recommended to create a python virtual environment and install the dependencies: diff --git a/explore-assistant-backend/terraform/bigquery_examples.tf b/explore-assistant-backend/terraform/bigquery_examples.tf index 3eb986ba..75fcec7c 100644 --- a/explore-assistant-backend/terraform/bigquery_examples.tf +++ b/explore-assistant-backend/terraform/bigquery_examples.tf @@ -67,3 +67,29 @@ resource "google_bigquery_job" "create_explore_assistant_refinement_examples_tab ignore_changes = [query, job_id] } } + +resource "google_bigquery_job" "create_explore_assistant_samples_table" { + job_id = "create_explore_assistant_samples_table-${formatdate("YYYYMMDDhhmmss", timestamp())}" + query { + query = <=10.0.0" @@ -13644,9 +13644,9 @@ "dev": true }, "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "engines": { "node": ">=8.3.0" diff --git a/explore-assistant-extension/src/App.tsx b/explore-assistant-extension/src/App.tsx index 795f235c..52be9521 100644 --- a/explore-assistant-extension/src/App.tsx +++ b/explore-assistant-extension/src/App.tsx @@ -1,24 +1,30 @@ import React, { useEffect } from 'react' import { hot } from 'react-hot-loader/root' import { Route, Switch, Redirect } from 'react-router-dom' -import { useDispatch } from 'react-redux' +import { useDispatch, useSelector } from 'react-redux' import { setExploreId, setExploreName, setModelName, } from './slices/assistantSlice' +import { RootState } from './store' import { useLookerFields } from './hooks/useLookerFields' import { useBigQueryExamples } from './hooks/useBigQueryExamples' import AgentPage from './pages/AgentPage' const ExploreApp = () => { const dispatch = useDispatch() + const { + exploreName, + modelName, + exploreId, + } = useSelector((state: RootState) => state.assistant) const LOOKER_EXPLORE_ID = - `${process.env.LOOKER_MODEL}/${process.env.LOOKER_EXPLORE}` || '' + exploreId || `${process.env.LOOKER_MODEL}/${process.env.LOOKER_EXPLORE}` || '' useEffect(() => { dispatch(setExploreId(LOOKER_EXPLORE_ID)) - dispatch(setModelName(process.env.LOOKER_MODEL || '')) - dispatch(setExploreName(process.env.LOOKER_EXPLORE || '')) + dispatch(setModelName(modelName || process.env.LOOKER_MODEL || '')) + dispatch(setExploreName(exploreName || process.env.LOOKER_EXPLORE || '')) }, []) diff --git a/explore-assistant-extension/src/components/SamplePrompts.tsx b/explore-assistant-extension/src/components/SamplePrompts.tsx index 2c88cef1..e96c1af7 100644 --- a/explore-assistant-extension/src/components/SamplePrompts.tsx +++ b/explore-assistant-extension/src/components/SamplePrompts.tsx @@ -1,47 +1,58 @@ import React from 'react' -import { useDispatch } from 'react-redux' +import { useDispatch, useSelector } from 'react-redux' import { resetChat, setIsChatMode, setQuery } from '../slices/assistantSlice' +import { RootState } from '../store' const SamplePrompts = () => { const dispatch = useDispatch() - const categorizedPrompts = [ - { - category: 'Cohorting', - prompt: 'Count of Users by first purchase date', - }, - { - category: 'Audience Building', - prompt: - 'Users who have purchased more than 100 dollars worth of Calvin Klein products and have purchased in the last 30 days', - }, - { - category: 'Period Comparison', - prompt: - 'Total revenue by category this year compared to last year in a line chart with year pivoted', - }, - ] + const { + examples, + exploreId, + } = useSelector((state: RootState) => state.assistant) + const [samplesLoaded, setSamplesLoaded] = React.useState(false) + + React.useEffect(() => { + if(examples.exploreSamples.length > 0) { + setSamplesLoaded(true) + console.log(examples.exploreSamples) + } + },[examples.exploreSamples]) const handleSubmit = (prompt: string) => { dispatch(resetChat()) dispatch(setQuery(prompt)) dispatch(setIsChatMode(true)) } - return ( -
- {categorizedPrompts.map((item, index: number) => ( -
{ - handleSubmit(item.prompt) - }} - > -
{item.prompt}
-
{item.category}
-
- ))} -
- ) + + if(samplesLoaded) { + const categorizedPrompts = JSON.parse( + examples.exploreSamples.filter( + explore => explore.explore_id === exploreId.replace("/",":") + ) + ?.[0] + ?.['samples'] ?? '[]' + ) + + return ( +
+ {categorizedPrompts.map((item, index: number) => ( +
{ + handleSubmit(item.prompt) + }} + > +
{item.prompt}
+
{item.category}
+
+ )) + } +
+ ) + } else { + return <> + } } export default SamplePrompts diff --git a/explore-assistant-extension/src/hooks/useBigQueryExamples.ts b/explore-assistant-extension/src/hooks/useBigQueryExamples.ts index 67401515..6783642e 100644 --- a/explore-assistant-extension/src/hooks/useBigQueryExamples.ts +++ b/explore-assistant-extension/src/hooks/useBigQueryExamples.ts @@ -1,18 +1,26 @@ import { useContext, useEffect } from 'react' -import { useDispatch } from 'react-redux' +import { useDispatch, useSelector } from 'react-redux' +import { RootState } from '../store' import { setExploreGenerationExamples, setExploreRefinementExamples, + setExploreSamples, + setExplores, + setBigQueryExamplesLoaded } from '../slices/assistantSlice' + import { ExtensionContext } from '@looker/extension-sdk-react' import process from 'process' import { useErrorBoundary } from 'react-error-boundary' export const useBigQueryExamples = () => { + const { exploreName, modelName} = useSelector( + (state: RootState) => state.assistant, + ) const connectionName = process.env.BIGQUERY_EXAMPLE_PROMPTS_CONNECTION_NAME || '' - const LOOKER_MODEL = process.env.LOOKER_MODEL || '' - const LOOKER_EXPLORE = process.env.LOOKER_EXPLORE || '' + const LOOKER_MODEL = modelName || process.env.LOOKER_MODEL || '' + const LOOKER_EXPLORE = exploreName || process.env.LOOKER_EXPLORE || '' const datasetName = process.env.BIGQUERY_EXAMPLE_PROMPTS_DATASET_NAME || 'explore_assistant' @@ -44,6 +52,18 @@ export const useBigQueryExamples = () => { } } + const getExplores = async () => { + const sql = ` + SELECT DISTINCT + explore_id + FROM + \`${datasetName}.explore_assistant_examples\` + ` + return runExampleQuery(sql).then((response) => { + dispatch(setExplores(response)) + }).catch((error) => showBoundary(error)) + } + const getExamplePrompts = async () => { const sql = ` SELECT @@ -72,9 +92,37 @@ export const useBigQueryExamples = () => { }).catch((error) => showBoundary(error)) } - // get the example prompts + const getSamples = async () => { + const sql = ` + SELECT + explore_id, + samples + FROM + \`${datasetName}.explore_assistant_samples\` + ` + return runExampleQuery(sql).then((response) => { + console.log(response) + const generationSamples = response + dispatch(setExploreSamples(generationSamples)) + }).catch((error) => showBoundary(error)) + } + + // only run once + useEffect(() => { + getExplores() + getSamples() + },[showBoundary]) + + // get the example prompts provide completion status useEffect(() => { - getExamplePrompts() - getRefinementPrompts() - }, [showBoundary]) + dispatch(setBigQueryExamplesLoaded(false)) + Promise.all([getExamplePrompts(), getRefinementPrompts()]) + .then(() => { + dispatch(setBigQueryExamplesLoaded(true)) + }) + .catch((error) => { + showBoundary(error) + dispatch(setBigQueryExamplesLoaded(true)) + }) + }, [modelName, exploreName, showBoundary]) } diff --git a/explore-assistant-extension/src/hooks/useLookerFields.ts b/explore-assistant-extension/src/hooks/useLookerFields.ts index a6dfef26..fb4b9c37 100644 --- a/explore-assistant-extension/src/hooks/useLookerFields.ts +++ b/explore-assistant-extension/src/hooks/useLookerFields.ts @@ -1,19 +1,28 @@ import { useContext, useEffect } from 'react' -import { useDispatch } from 'react-redux' -import { setDimensions, setMeasures } from '../slices/assistantSlice' +import { useDispatch, useSelector } from 'react-redux' +import { setDimensions, setMeasures, setLookerFieldsLoaded } from '../slices/assistantSlice' +import { RootState } from '../store' import { ExtensionContext } from '@looker/extension-sdk-react' import process from 'process' import { useErrorBoundary } from 'react-error-boundary' export const useLookerFields = () => { - const lookerModel = process.env.LOOKER_MODEL || '' - const lookerExplore = process.env.LOOKER_EXPLORE || '' + const { exploreName, modelName } = useSelector( + (state: RootState) => state.assistant, + ) + const lookerModel = modelName || process.env.LOOKER_MODEL || '' + const lookerExplore = exploreName || process.env.LOOKER_EXPLORE || '' const dispatch = useDispatch() const { showBoundary } = useErrorBoundary(); const { core40SDK } = useContext(ExtensionContext) - + + // load lookml metadata and provide completion status useEffect(() => { + if(!lookerModel || lookerModel === '' || !lookerExplore || lookerModel === '') { + showBoundary({message: "Default Looker Model or Explore is blank or unspecified"}) + } + dispatch(setLookerFieldsLoaded(false)) core40SDK .ok( core40SDK.lookml_model_explore({ @@ -48,9 +57,11 @@ export const useLookerFields = () => { dispatch(setDimensions(dimensions)) dispatch(setMeasures(measures)) + dispatch(setLookerFieldsLoaded(true)) }) .catch((error) => { showBoundary(error) + dispatch(setLookerFieldsLoaded(true)) }) - }, [dispatch,showBoundary, lookerModel, lookerExplore]) // Dependencies array to avoid unnecessary re-executions + }, [dispatch,showBoundary, modelName, exploreName]) // Dependencies array to avoid unnecessary re-executions } diff --git a/explore-assistant-extension/src/hooks/useSendVertexMessage.ts b/explore-assistant-extension/src/hooks/useSendVertexMessage.ts index 6ed1f4c1..ed58acdf 100644 --- a/explore-assistant-extension/src/hooks/useSendVertexMessage.ts +++ b/explore-assistant-extension/src/hooks/useSendVertexMessage.ts @@ -71,8 +71,14 @@ const useSendVertexMessage = () => { const VERTEX_BIGQUERY_MODEL_ID = process.env.VERTEX_BIGQUERY_MODEL_ID || '' const { core40SDK } = useContext(ExtensionContext) - const { dimensions, measures, messageThread, exploreName, modelName } = + const { dimensions, measures, messageThread, exploreName, modelName, bigQueryExamplesLoaded, lookerFieldsLoaded } = useSelector((state: RootState) => state.assistant) + + const isDataLoaded = bigQueryExamplesLoaded && lookerFieldsLoaded + + const settings = useSelector( + (state) => state.assistant.settings, + ) const settings = useSelector( (state) => state.assistant.settings, @@ -321,74 +327,79 @@ ${exploreRefinementExamples ) const generateExploreUrl = useCallback( - async (prompt: string) => { - const contents = ` - Context - ---------- - - You are a developer who would transalate questions to a structured Looker URL query based on the following instructions. - - Instructions: - - choose only the fields in the below lookml metadata - - prioritize the field description, label, tags, and name for what field(s) to use for a given description - - generate only one answer, no more. - - use the Examples (at the bottom) for guidance on how to structure the Looker url query - - try to avoid adding dynamic_fields, provide them when very similar example is found in the bottom - - never respond with sql, always return an looker explore url as a single string - - response should start with fields= , as in the Examples section at the bottom - - LookML Metadata - ---------- - - Dimensions Used to group by information (follow the instructions in tags when using a specific field; if map used include a location or lat long dimension;): - - ${dimensions.map(formatContent).join('\n')} - - Measures are used to perform calculations (if top, bottom, total, sum, etc. are used include a measure): - - ${measures.map(formatContent).join('\n')} - - Example - ---------- - - ${exploreGenerationExamples - .map((item) => `input: "${item.input}" ; output: ${item.output}`) - .join('\n')} + async (prompt: string, dimensions: any[], measures: any[], exploreGenerationExamples: any[]) => { + try { + console.log("From Vertex: ", exploreName, modelName, dimensions, measures, exploreGenerationExamples,isDataLoaded) + const contents = ` + Context + ---------- - Input - ---------- - ${prompt} + You are a developer who would transalate questions to a structured Looker URL query based on the following instructions. - - Output - ---------- - ` - const parameters = { - max_output_tokens: 1000, - } - console.log(contents) - const response = await sendMessage(contents, parameters) - - const unquoteResponse = (response: string) => { - return response - .substring(response.indexOf('fields=')) - .replace(/^`+|`+$/g, '') - .trim() - } - const cleanResponse = unquoteResponse(response) - console.log(cleanResponse) + Instructions: + - choose only the fields in the below lookml metadata + - prioritize the field description, label, tags, and name for what field(s) to use for a given description + - generate only one answer, no more. + - use the Examples (at the bottom) for guidance on how to structure the Looker url query + - try to avoid adding dynamic_fields, provide them when very similar example is found in the bottom + - never respond with sql, always return an looker explore url as a single string + - response should start with fields= , as in the Examples section at the bottom - let toggleString = '&toggle=dat,pik,vis' - if(settings['show_explore_data'].value) { - toggleString = '&toggle=pik,vis' - } + LookML Metadata + ---------- + + Dimensions Used to group by information (follow the instructions in tags when using a specific field; if map used include a location or lat long dimension;): + + ${dimensions.map(formatContent).join('\n')} + + Measures are used to perform calculations (if top, bottom, total, sum, etc. are used include a measure): + + ${measures.map(formatContent).join('\n')} + + Example + ---------- + + ${exploreGenerationExamples + .map((item) => `input: "${item.input}" ; output: ${item.output}`) + .join('\n')} - const newExploreUrl = cleanResponse + toggleString + Input + ---------- + ${prompt} + Output + ---------- + ` + const parameters = { + max_output_tokens: 1000, + } + console.log(contents) + const response = await sendMessage(contents, parameters) + + const unquoteResponse = (response: string) => { + return response + .substring(response.indexOf('fields=')) + .replace(/^`+|`+$/g, '') + .trim() + } + const cleanResponse = unquoteResponse(response) + console.log(cleanResponse) + + let toggleString = '&toggle=dat,pik,vis' + if(settings['show_explore_data'].value) { + toggleString = '&toggle=pik,vis' + } + + const newExploreUrl = cleanResponse + toggleString - return newExploreUrl + + return newExploreUrl + } catch (error) { + console.error("Error waiting for data (lookml fields & training examples) to load:", error); + showBoundary({message: "Error waiting for data (lookml fields & training examples) to load:", error}) + } }, - [dimensions, measures, exploreGenerationExamples, settings], + [settings, exploreName, modelName], ) const sendMessage = async (message: string, parameters: ModelParameters) => { diff --git a/explore-assistant-extension/src/pages/AgentPage/Settings.tsx b/explore-assistant-extension/src/pages/AgentPage/Settings.tsx index 749ef86d..ee9872cd 100644 --- a/explore-assistant-extension/src/pages/AgentPage/Settings.tsx +++ b/explore-assistant-extension/src/pages/AgentPage/Settings.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React from 'react' import { Modal, Box, @@ -8,10 +8,13 @@ import { ListItem, ListItemText, ListItemSecondaryAction, + MenuItem, + Select, + SelectChangeEvent } from '@mui/material' import { useSelector, useDispatch } from 'react-redux' import { RootState } from '../../store' -import { setSetting, Settings } from '../../slices/assistantSlice' +import { setSetting, setExploreName, setExploreId, setModelName, resetChat } from '../../slices/assistantSlice' interface SettingsModalProps { open: boolean @@ -20,10 +23,18 @@ interface SettingsModalProps { const SettingsModal: React.FC = ({ open, onClose }) => { const dispatch = useDispatch() - const settings = useSelector( - (state) => state.assistant.settings, + const { settings, exploreId, explores } = useSelector( + (state: RootState) => state.assistant, ) + const setSelectedExplore = (e: SelectChangeEvent) => { + const parsedExploreID = e.target.value.split(":") + dispatch(setExploreName(parsedExploreID[1])) + dispatch(setModelName(parsedExploreID[0])) + dispatch(setExploreId(e.target.value.replace(":","/"))) + dispatch(resetChat()) + } + const handleToggle = (id: string) => { dispatch( setSetting({ @@ -53,7 +64,7 @@ const SettingsModal: React.FC = ({ open, onClose }) => { {Object.entries(settings).map(([id, setting]) => ( - + = ({ open, onClose }) => { /> handleToggle(id)} - checked={setting.value} - inputProps={{ 'aria-labelledby': `switch-${id}` }} + edge="end" + onChange={() => handleToggle(id)} + checked={setting.value} + inputProps={{ 'aria-labelledby': `switch-${id}` }} /> ))} + + + + + + diff --git a/explore-assistant-extension/src/pages/AgentPage/Sidebar.tsx b/explore-assistant-extension/src/pages/AgentPage/Sidebar.tsx index eb5cdb01..ac5ceaf0 100644 --- a/explore-assistant-extension/src/pages/AgentPage/Sidebar.tsx +++ b/explore-assistant-extension/src/pages/AgentPage/Sidebar.tsx @@ -10,6 +10,11 @@ import { resetChat, setIsChatMode, setQuery, + setExploreId, + setExploreName, + setModelName, + HistoryItemPayload, + setSidebarMessage } from '../../slices/assistantSlice' import { RootState } from '../../store' import SettingsModal from './Settings' @@ -23,7 +28,7 @@ const Sidebar = ({ expanded, toggleDrawer }: SidebarProps) => { const dispatch = useDispatch() const [isExpanded, setIsExpanded] = React.useState(expanded) const [isSettingsOpen, setIsSettingsOpen] = React.useState(false) - const { isChatMode, isQuerying, history } = useSelector( + const { isChatMode, isQuerying, history, exploreId, bigQueryExamplesLoaded, lookerFieldsLoaded, sidebarMessage } = useSelector( (state: RootState) => state.assistant, ) @@ -56,11 +61,25 @@ const Sidebar = ({ expanded, toggleDrawer }: SidebarProps) => { } } - const handleHistoryClick = (message: string) => { - dispatch(resetChat()) - dispatch(setQuery(message)) - dispatch(setIsChatMode(true)) - } + const handleHistoryClick = async (message: HistoryItemPayload) => { + // Combine related state updates + const [modelName, exploreName] = message.exploreId.split("/"); + dispatch(setExploreId(message.exploreId)) + dispatch(setExploreName(exploreName)) + dispatch(setModelName(modelName)) + dispatch(setSidebarMessage(message.message)) + + console.log("Current: ", exploreId, " Next: ", message); + }; + + React.useEffect(() => { + if(bigQueryExamplesLoaded && lookerFieldsLoaded && sidebarMessage !== '') { + dispatch(resetChat()) + dispatch(setQuery(sidebarMessage)) + dispatch(setIsChatMode(true)) + + } + },[dispatch, sidebarMessage, bigQueryExamplesLoaded, lookerFieldsLoaded]) const handleClearHistory = () => { dispatch(clearHistory()) @@ -142,7 +161,7 @@ const Sidebar = ({ expanded, toggleDrawer }: SidebarProps) => {
handleHistoryClick(item.message)} + onClick={() => handleHistoryClick(item)} >
{ dimensions, measures, examples, + exploreId, + bigQueryExamplesLoaded, + lookerFieldsLoaded } = useSelector((state: RootState) => state.assistant) const submitMessage = useCallback(async () => { + dispatch(setSidebarMessage('')) dispatch(addPrompt(query)) dispatch(setIsQuerying(true)) @@ -66,16 +70,17 @@ const AgentPage = () => { return } - // update the history of the current thread + // update the history of the current thread and track explore id for update explore state when going back to prior conversations if(currentExploreThread.messages.length > 0) { // edit existing - dispatch(updateLastHistoryEntry(promptSummary)) + dispatch(updateLastHistoryEntry({ message: promptSummary, exploreId: exploreId })) } else { // create new - dispatch(addToHistory(promptSummary)) + dispatch(addToHistory({ message: promptSummary, exploreId: exploreId })) } - - const newExploreUrl = await generateExploreUrl(promptSummary) + + const newExploreUrl = await generateExploreUrl(promptSummary,dimensions, measures, examples.exploreGenerationExamples) + console.log("New Explore URL: ", newExploreUrl) dispatch(setIsQuerying(false)) dispatch(setQuery('')) @@ -102,16 +107,21 @@ const AgentPage = () => { }), ) } - }, [query]) + }, [generateExploreUrl, dispatch, query, dimensions, measures, examples]) + + const isDataLoaded = bigQueryExamplesLoaded && lookerFieldsLoaded useEffect(() => { - if (!query) { + if (!query || query === '') { return } - submitMessage() - endOfMessagesRef.current?.scrollIntoView({ behavior: 'smooth' }) - }, [query]) + if(query !== '' && isDataLoaded) { + submitMessage() + endOfMessagesRef.current?.scrollIntoView({ behavior: 'smooth' }) + } + + }, [query,isDataLoaded, submitMessage]) const toggleDrawer = () => { setExpanded(!expanded) @@ -159,9 +169,24 @@ const AgentPage = () => { sidePanel.isSidePanelOpen ? 'w-2/5' : 'w-full', )} > -
+
- + {!isDataLoaded + ? ( +
+

+ + Hello. + +

+

Loading the conversation and LookML Metadata...

+
+ +
+
+ ) + : + }
) => { state.sidePanel.exploreUrl = action.payload }, - updateLastHistoryEntry: (state, action: PayloadAction) => { + updateLastHistoryEntry: (state, action: PayloadAction) => { state.history[state.history.length - 1] = { - message: action.payload, + message: action.payload.message, createdAt: Date.now(), + exploreId: action.payload.exploreId } }, - addToHistory: (state, action: PayloadAction) => { + addToHistory: (state, action: PayloadAction) => { state.history.push({ - message: action.payload, + message: action.payload.message, createdAt: Date.now(), + exploreId: action.payload.exploreId }) }, setHistory: (state, action: PayloadAction) => { @@ -202,18 +231,42 @@ export const assistantSlice = createSlice({ setModelName: (state, action: PayloadAction) => { state.modelName = action.payload }, + setExplores: (state, action: PayloadAction) => { + state.explores = action.payload + }, setExploreGenerationExamples( state, - action: PayloadAction, + action: PayloadAction, ) { state.examples.exploreGenerationExamples = action.payload }, setExploreRefinementExamples( state, - action: PayloadAction, + action: PayloadAction, ) { state.examples.exploreRefinementExamples = action.payload }, + setExploreSamples( + state, + action: PayloadAction, + ) { + state.examples.exploreSamples = action.payload + }, + setLookerFieldsLoaded: ( + state, + action: PayloadAction + ) => { + state.lookerFieldsLoaded = action.payload + }, + setBigQueryExamplesLoaded: ( + state, + action: PayloadAction + ) => { + state.bigQueryExamplesLoaded = action.payload + }, + setSidebarMessage: (state, action: PayloadAction) => { + state.sidebarMessage = action.payload + }, }, }) @@ -235,8 +288,14 @@ export const { setExploreId, setExploreName, setModelName, + setExplores, setExploreGenerationExamples, setExploreRefinementExamples, + setExploreSamples, + + setBigQueryExamplesLoaded, + setLookerFieldsLoaded, + setSidebarMessage, openSidePanel, closeSidePanel,