Skip to content

Commit

Permalink
feat: add @mention to select a table for the command input (#1432)
Browse files Browse the repository at this point in the history
* feat: add @mention support for the command input

* address comments

* fix table selector disappear issue

* comments

* remove id

* use suggest

* add comment
  • Loading branch information
jczhong84 authored Apr 10, 2024
1 parent 7fa6eb6 commit 067d96d
Show file tree
Hide file tree
Showing 14 changed files with 241 additions and 96 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "querybook",
"version": "3.33.1",
"version": "3.33.2",
"description": "A Big Data Webapp",
"private": true,
"scripts": {
Expand Down Expand Up @@ -84,6 +84,7 @@
"react-hot-toast": "^2.4.0",
"react-json-view": "^1.21.3",
"react-lazyload": "^3.2.0",
"react-mentions": "^4.4.10",
"react-redux": "7.2.4",
"react-router-dom": "5.1.2",
"react-select": "4.3.1",
Expand Down
2 changes: 1 addition & 1 deletion querybook/config/elasticsearch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ tables:
full_name_ngram:
type: text
analyzer: edge_ngram_lowercase
completion_name:
completion_name: # to be deprecated
type: completion
analyzer: keyword
contexts:
Expand Down
14 changes: 4 additions & 10 deletions querybook/server/datasources/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,20 +130,14 @@ def vector_search_tables(


@register("/suggest/<int:metastore_id>/tables/", methods=["GET"])
def suggest_tables(metastore_id, prefix, limit=10):
def suggest_tables(metastore_id, keyword, limit=10):
api_assert(limit is None or limit <= 100, "Requesting too many tables")
verify_metastore_permission(metastore_id)

query = construct_suggest_table_query(prefix, limit, metastore_id)
query = construct_suggest_table_query(keyword, limit, metastore_id)
options = get_matching_suggestions(query, ES_CONFIG["tables"]["index_name"])
texts = [
"{}.{}".format(
option.get("_source", {}).get("schema", ""),
option.get("_source", {}).get("name", ""),
)
for option in options
]
return texts
tables = [option.get("_source", {}).get("full_name") for option in options]
return tables


# /search/ but it is actually suggest
Expand Down
4 changes: 1 addition & 3 deletions querybook/server/lib/elasticsearch/search_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,6 @@ def get_matching_suggestions(query: Union[str, Dict], index_name: str):
if result is None:
result = {}

options = next(iter(result.get("suggest", {}).get("suggest", [])), {}).get(
"options", []
)
options = result.get("hits", {}).get("hits", [])

return options
17 changes: 9 additions & 8 deletions querybook/server/lib/elasticsearch/suggest_table.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
def construct_suggest_table_query(
prefix: str,
keyword: str,
limit: int,
metastore_id: int,
):
return {
"suggest": {
"suggest": {
"text": prefix,
"completion": {
"field": "completion_name",
"size": limit,
"contexts": {"metastore_id": metastore_id},
"from": 0,
"size": limit,
"query": {
"bool": {
"must": {
"match": {"full_name_ngram": {"query": keyword, "operator": "and"}}
},
"filter": {"match": {"metastore_id": metastore_id}},
}
},
"_source": ["id", "full_name"],
}
1 change: 1 addition & 0 deletions querybook/server/logic/elasticsearch.py
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,7 @@ def get_completion_name():
"name": table_name,
"full_name": full_name,
"full_name_ngram": full_name,
# To be deprecated: completion_name is not used anymore
"completion_name": get_completion_name,
"description": get_table_description,
"created_at": lambda: DATETIME_TO_UTC(table.created_at),
Expand Down
50 changes: 34 additions & 16 deletions querybook/webapp/components/AIAssistant/AICommandBar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { set, uniq } from 'lodash';
import React, { forwardRef, useCallback, useEffect, useState } from 'react';
import { uniq } from 'lodash';
import React, {
forwardRef,
useCallback,
useEffect,
useMemo,
useState,
} from 'react';

import { AICommandInput } from 'components/AIAssistant/AICommandInput';
import { AICommandResultView } from 'components/AIAssistant/AICommandResultView';
Expand All @@ -11,14 +17,11 @@ import {
} from 'const/command';
import { IQueryEngine } from 'const/queryEngine';
import { CommandRunner, useCommand } from 'hooks/useCommand';
import { useEvent } from 'hooks/useEvent';
import { useForwardedRef } from 'hooks/useForwardedRef';
import { trackClick } from 'lib/analytics';
import { TableToken } from 'lib/sql-helper/sql-lexer';
import { matchKeyPress } from 'lib/utils/keyboard';
import { analyzeCode } from 'lib/web-worker';
import { Button, TextButton } from 'ui/Button/Button';
import { IconButton } from 'ui/Button/IconButton';
import { Button } from 'ui/Button/Button';
import { Message } from 'ui/Message/Message';
import { IResizableTextareaHandles } from 'ui/ResizableTextArea/ResizableTextArea';
import { StyledText } from 'ui/StyledText/StyledText';
Expand Down Expand Up @@ -65,7 +68,8 @@ export const AICommandBar: React.FC<IQueryCellCommandBarProps> = forwardRef(
(cmd) => cmd.name === (query ? 'edit' : 'generate')
);
const tablesInQuery = useTablesInQuery(query, queryEngine.language);
const [tables, setTables] = useState(tablesInQuery);
const [tables, setTables] = useState<string[]>([]);
const [mentionedTables, setMentionedTables] = useState<string[]>([]);
const commandInputRef = useForwardedRef<IResizableTextareaHandles>(ref);
const [showPopupView, setShowPopupView] = useState(false);
const [command, setCommand] =
Expand All @@ -77,6 +81,14 @@ export const AICommandBar: React.FC<IQueryCellCommandBarProps> = forwardRef(
);
const [showConfirm, setShowConfirm] = useState(false);

const finalTablesToUse = useMemo(() => {
if (mentionedTables.length > 0) {
return mentionedTables;
} else {
return uniq([...tablesInQuery, ...tables]);
}
}, [tablesInQuery, mentionedTables, tables]);

const {
runCommand,
isRunning,
Expand All @@ -86,8 +98,14 @@ export const AICommandBar: React.FC<IQueryCellCommandBarProps> = forwardRef(
} = useCommand(command, commandRunner);

useEffect(() => {
setTables((tables) => uniq([...tablesInQuery, ...tables]));
}, [tablesInQuery]);
if (!query && mentionedTables.length === 1) {
onUpdateQuery(
`SELECT * FROM ${mentionedTables[0]} LIMIT 10`,
false
);
onFormatQuery();
}
}, [mentionedTables]);

useEffect(() => {
if (command.name === 'format') {
Expand All @@ -96,7 +114,7 @@ export const AICommandBar: React.FC<IQueryCellCommandBarProps> = forwardRef(
} else if (command.name === 'generate' || command.name === 'edit') {
setCommandKwargs({
query_engine_id: queryEngine.id,
tables: tables,
tables: finalTablesToUse,
question: commandInputValue,
original_query: command.name === 'generate' ? '' : query,
});
Expand All @@ -122,25 +140,22 @@ export const AICommandBar: React.FC<IQueryCellCommandBarProps> = forwardRef(
aux: {
mode: command.name,
question: commandKwargs.question,
tables,
tables: finalTablesToUse,
},
});
}
}, [command, runCommand, setShowPopupView, commandKwargs]);

const getCommandResultView = () => {
if (!commandResult) {
return null;
}

if (command.name === 'generate' || command.name === 'edit') {
return (
<AICommandResultView
command={command}
commandKwargs={commandKwargs}
metastoreId={queryEngine.metastore_id}
commandResult={commandResult}
tables={tables}
tables={finalTablesToUse}
hasMentionedTables={mentionedTables.length > 0}
originalQuery={query}
isStreaming={isRunning}
onContinue={handleCommand}
Expand Down Expand Up @@ -226,6 +241,9 @@ export const AICommandBar: React.FC<IQueryCellCommandBarProps> = forwardRef(
);
setCommandInputValue(inputValue);
}}
mentionedTables={mentionedTables}
onMentionedTablesChange={setMentionedTables}
metastoreId={queryEngine.metastore_id}
onSubmit={handleCommand}
running={isRunning}
cancelGeneration={cancelCommand}
Expand Down
14 changes: 1 addition & 13 deletions querybook/webapp/components/AIAssistant/AICommandInput.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@
background-color: var(--bg-lightest);
border-radius: var(--border-radius-sm);

&:hover {
background-color: var(--bg-hover);
}

.stars-icon {
position: relative;
color: var(--icon);
Expand Down Expand Up @@ -41,15 +37,6 @@
color: var(--color-pink-dark);
}

.question-text-area {
flex: 1;
min-width: auto;
line-height: 24px;
min-height: 40px;
overflow: hidden;
padding: 8px 8px;
}

.button {
height: 28px;
margin: 6px;
Expand All @@ -59,6 +46,7 @@
.AICommandInput-popover {
border-radius: var(--border-radius-sm);
overflow: hidden;
box-shadow: 0 0px 4px var(--bg-dark);

.command-item {
display: flex;
Expand Down
Loading

0 comments on commit 067d96d

Please sign in to comment.