chore(wren-ui): Remove candidate summary & add correcting state to prompt (#913)

This commit is contained in:
Shimin
2024-11-18 16:08:57 +08:00
committed by Andy Yen
parent f61007c42b
commit c92a100376
17 changed files with 158 additions and 215 deletions
@@ -0,0 +1,22 @@
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function (knex) {
return knex.schema.alterTable('thread_response', function (table) {
table.dropColumn('summary');
});
};
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function (knex) {
return knex.schema.alterTable('thread_response', function (table) {
table
.string('summary')
.nullable()
.comment('the summary of the thread response');
});
};
@@ -28,6 +28,7 @@ export type AskingTaskInput = {
};
export enum AskingTaskStatus {
CORRECTING = 'CORRECTING',
FAILED = 'FAILED',
FINISHED = 'FINISHED',
GENERATING = 'GENERATING',
@@ -98,14 +99,12 @@ export type CreateSimpleMetricInput = {
export type CreateThreadInput = {
question?: InputMaybe<Scalars['String']>;
sql?: InputMaybe<Scalars['String']>;
summary?: InputMaybe<Scalars['String']>;
viewId?: InputMaybe<Scalars['Int']>;
};
export type CreateThreadResponseInput = {
question?: InputMaybe<Scalars['String']>;
sql?: InputMaybe<Scalars['String']>;
summary?: InputMaybe<Scalars['String']>;
viewId?: InputMaybe<Scalars['Int']>;
};
@@ -234,7 +233,6 @@ export type DetailedThread = {
responses: Array<ThreadResponse>;
/** @deprecated Doesn't seem to be reasonable to put a sql in a thread */
sql: Scalars['String'];
summary: Scalars['String'];
};
export type Diagram = {
@@ -849,7 +847,6 @@ export type ResolveSchemaChangeWhereInput = {
export type ResultCandidate = {
__typename?: 'ResultCandidate';
sql: Scalars['String'];
summary: Scalars['String'];
type: ResultCandidateType;
view?: Maybe<ViewInfo>;
};
@@ -955,7 +952,6 @@ export type ThreadResponse = {
id: Scalars['Int'];
question: Scalars['String'];
status: AskingTaskStatus;
summary: Scalars['String'];
};
export type ThreadResponseDetail = {
@@ -5,7 +5,7 @@ import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type CommonErrorFragment = { __typename?: 'Error', code?: string | null, shortMessage?: string | null, message?: string | null, stacktrace?: Array<string | null> | null };
export type CommonResponseFragment = { __typename?: 'ThreadResponse', id: number, question: string, summary: string, status: Types.AskingTaskStatus, detail?: { __typename?: 'ThreadResponseDetail', sql?: string | null, description?: string | null, steps: Array<{ __typename?: 'DetailStep', summary: string, sql: string, cteName?: string | null }>, view?: { __typename?: 'ViewInfo', id: number, name: string, statement: string, displayName: string } | null } | null };
export type CommonResponseFragment = { __typename?: 'ThreadResponse', id: number, question: string, status: Types.AskingTaskStatus, detail?: { __typename?: 'ThreadResponseDetail', sql?: string | null, description?: string | null, steps: Array<{ __typename?: 'DetailStep', summary: string, sql: string, cteName?: string | null }>, view?: { __typename?: 'ViewInfo', id: number, name: string, statement: string, displayName: string } | null } | null };
export type SuggestedQuestionsQueryVariables = Types.Exact<{ [key: string]: never; }>;
@@ -17,7 +17,7 @@ export type AskingTaskQueryVariables = Types.Exact<{
}>;
export type AskingTaskQuery = { __typename?: 'Query', askingTask: { __typename?: 'AskingTask', status: Types.AskingTaskStatus, type?: Types.AskingTaskType | null, candidates: Array<{ __typename?: 'ResultCandidate', sql: string, summary: string, type: Types.ResultCandidateType, view?: { __typename?: 'ViewInfo', id: number, name: string, statement: string, displayName: string } | null }>, error?: { __typename?: 'Error', code?: string | null, shortMessage?: string | null, message?: string | null, stacktrace?: Array<string | null> | null } | null } };
export type AskingTaskQuery = { __typename?: 'Query', askingTask: { __typename?: 'AskingTask', status: Types.AskingTaskStatus, type?: Types.AskingTaskType | null, candidates: Array<{ __typename?: 'ResultCandidate', sql: string, type: Types.ResultCandidateType, view?: { __typename?: 'ViewInfo', id: number, name: string, statement: string, displayName: string } | null }>, error?: { __typename?: 'Error', code?: string | null, shortMessage?: string | null, message?: string | null, stacktrace?: Array<string | null> | null } | null } };
export type ThreadsQueryVariables = Types.Exact<{ [key: string]: never; }>;
@@ -29,14 +29,14 @@ export type ThreadQueryVariables = Types.Exact<{
}>;
export type ThreadQuery = { __typename?: 'Query', thread: { __typename?: 'DetailedThread', id: number, sql: string, summary: string, responses: Array<{ __typename?: 'ThreadResponse', id: number, question: string, summary: string, status: Types.AskingTaskStatus, error?: { __typename?: 'Error', code?: string | null, shortMessage?: string | null, message?: string | null, stacktrace?: Array<string | null> | null } | null, detail?: { __typename?: 'ThreadResponseDetail', sql?: string | null, description?: string | null, steps: Array<{ __typename?: 'DetailStep', summary: string, sql: string, cteName?: string | null }>, view?: { __typename?: 'ViewInfo', id: number, name: string, statement: string, displayName: string } | null } | null }> } };
export type ThreadQuery = { __typename?: 'Query', thread: { __typename?: 'DetailedThread', id: number, sql: string, responses: Array<{ __typename?: 'ThreadResponse', id: number, question: string, status: Types.AskingTaskStatus, error?: { __typename?: 'Error', code?: string | null, shortMessage?: string | null, message?: string | null, stacktrace?: Array<string | null> | null } | null, detail?: { __typename?: 'ThreadResponseDetail', sql?: string | null, description?: string | null, steps: Array<{ __typename?: 'DetailStep', summary: string, sql: string, cteName?: string | null }>, view?: { __typename?: 'ViewInfo', id: number, name: string, statement: string, displayName: string } | null } | null }> } };
export type ThreadResponseQueryVariables = Types.Exact<{
responseId: Types.Scalars['Int'];
}>;
export type ThreadResponseQuery = { __typename?: 'Query', threadResponse: { __typename?: 'ThreadResponse', id: number, question: string, summary: string, status: Types.AskingTaskStatus, error?: { __typename?: 'Error', code?: string | null, shortMessage?: string | null, message?: string | null, stacktrace?: Array<string | null> | null } | null, detail?: { __typename?: 'ThreadResponseDetail', sql?: string | null, description?: string | null, steps: Array<{ __typename?: 'DetailStep', summary: string, sql: string, cteName?: string | null }>, view?: { __typename?: 'ViewInfo', id: number, name: string, statement: string, displayName: string } | null } | null } };
export type ThreadResponseQuery = { __typename?: 'Query', threadResponse: { __typename?: 'ThreadResponse', id: number, question: string, status: Types.AskingTaskStatus, error?: { __typename?: 'Error', code?: string | null, shortMessage?: string | null, message?: string | null, stacktrace?: Array<string | null> | null } | null, detail?: { __typename?: 'ThreadResponseDetail', sql?: string | null, description?: string | null, steps: Array<{ __typename?: 'DetailStep', summary: string, sql: string, cteName?: string | null }>, view?: { __typename?: 'ViewInfo', id: number, name: string, statement: string, displayName: string } | null } | null } };
export type CreateAskingTaskMutationVariables = Types.Exact<{
data: Types.AskingTaskInput;
@@ -57,7 +57,7 @@ export type CreateThreadMutationVariables = Types.Exact<{
}>;
export type CreateThreadMutation = { __typename?: 'Mutation', createThread: { __typename?: 'Thread', id: number, sql: string, summary: string } };
export type CreateThreadMutation = { __typename?: 'Mutation', createThread: { __typename?: 'Thread', id: number, sql: string } };
export type CreateThreadResponseMutationVariables = Types.Exact<{
threadId: Types.Scalars['Int'];
@@ -65,7 +65,7 @@ export type CreateThreadResponseMutationVariables = Types.Exact<{
}>;
export type CreateThreadResponseMutation = { __typename?: 'Mutation', createThreadResponse: { __typename?: 'ThreadResponse', id: number, question: string, summary: string, status: Types.AskingTaskStatus, error?: { __typename?: 'Error', code?: string | null, shortMessage?: string | null, message?: string | null, stacktrace?: Array<string | null> | null } | null, detail?: { __typename?: 'ThreadResponseDetail', sql?: string | null, description?: string | null, steps: Array<{ __typename?: 'DetailStep', summary: string, sql: string, cteName?: string | null }>, view?: { __typename?: 'ViewInfo', id: number, name: string, statement: string, displayName: string } | null } | null } };
export type CreateThreadResponseMutation = { __typename?: 'Mutation', createThreadResponse: { __typename?: 'ThreadResponse', id: number, question: string, status: Types.AskingTaskStatus, error?: { __typename?: 'Error', code?: string | null, shortMessage?: string | null, message?: string | null, stacktrace?: Array<string | null> | null } | null, detail?: { __typename?: 'ThreadResponseDetail', sql?: string | null, description?: string | null, steps: Array<{ __typename?: 'DetailStep', summary: string, sql: string, cteName?: string | null }>, view?: { __typename?: 'ViewInfo', id: number, name: string, statement: string, displayName: string } | null } | null } };
export type UpdateThreadMutationVariables = Types.Exact<{
where: Types.ThreadUniqueWhereInput;
@@ -122,7 +122,6 @@ export const CommonResponseFragmentDoc = gql`
fragment CommonResponse on ThreadResponse {
id
question
summary
status
detail {
sql
@@ -185,7 +184,6 @@ export const AskingTaskDocument = gql`
type
candidates {
sql
summary
type
view {
id
@@ -268,7 +266,6 @@ export const ThreadDocument = gql`
thread(threadId: $threadId) {
id
sql
summary
responses {
...CommonResponse
error {
@@ -415,7 +412,6 @@ export const CreateThreadDocument = gql`
createThread(data: $data) {
id
sql
summary
}
}
`;
@@ -13,7 +13,6 @@ const COMMON_RESPONSE = gql`
fragment CommonResponse on ThreadResponse {
id
question
summary
status
detail {
sql
@@ -51,7 +50,6 @@ export const ASKING_TASK = gql`
type
candidates {
sql
summary
type
view {
id
@@ -82,7 +80,6 @@ export const THREAD = gql`
thread(threadId: $threadId) {
id
sql
summary
responses {
...CommonResponse
error {
@@ -127,7 +124,6 @@ export const CREATE_THREAD = gql`
createThread(data: $data) {
id
sql
summary
}
}
`;
@@ -34,7 +34,7 @@ export interface IWrenAIAdaptor {
/**
* Ask AI service a question.
* AI service will return anwser candidates containing sql and summary.
* AI service will return anwser candidates containing sql.
* 1. use ask() to ask a question, AI service will return a queryId
* 2. use getAskResult() to get the result of the queryId
* 3. use cancelAsk() to cancel the query
@@ -72,7 +72,7 @@ export class WrenAIAdaptor implements IWrenAIAdaptor {
/**
* Ask AI service a question.
* AI service will return anwser candidates containing sql and summary.
* AI service will return anwser candidates containing sql.
*/
public async ask(input: AskInput): Promise<AsyncQueryResponse> {
@@ -307,7 +307,6 @@ export class WrenAIAdaptor implements IWrenAIAdaptor {
const candidates = (body?.response || []).map((candidate: any) => ({
type: candidate?.type?.toUpperCase() as AskCandidateType,
sql: candidate.sql,
summary: candidate.summary,
viewId: candidate?.viewId ? Number(candidate.viewId) : null,
}));
+1 -3
View File
@@ -50,7 +50,6 @@ export interface AskStep {
export interface AskHistory {
sql: string;
summary: string;
steps: Array<AskStep>;
}
@@ -73,6 +72,7 @@ export enum AskResultStatus {
UNDERSTANDING = 'UNDERSTANDING',
SEARCHING = 'SEARCHING',
GENERATING = 'GENERATING',
CORRECTING = 'CORRECTING',
FINISHED = 'FINISHED',
FAILED = 'FAILED',
STOPPED = 'STOPPED',
@@ -101,7 +101,6 @@ export interface AskResponse<R, S> {
export interface AskDetailInput {
query: string;
sql: string;
summary: string;
configurations?: AskConfigurations;
}
@@ -117,7 +116,6 @@ export type AskResult = AskResponse<
Array<{
type: AskCandidateType;
sql: string;
summary: string;
viewId?: number | null;
}>,
AskResultStatus
@@ -24,14 +24,12 @@ export interface ThreadResponse {
threadId: number; // Reference to thread.id
queryId: string; // Thread response query ID
question: string; // Thread response question
summary: string; // Summary comes from the user's original question
status: string; // Thread response status
detail: ThreadResponseDetail; // Thread response detail
error: object; // Thread response error
}
export interface ThreadResponseWithThreadContext extends ThreadResponse {
threadSummary: string; // Thread summary is generated by AI first, and can be updated by the user
sql: string;
}
@@ -55,7 +53,6 @@ export class ThreadResponseRepository
const query = this.knex(this.tableName)
.select('thread_response.*')
.select('thread.sql as sql')
.select('thread.summary as thread_summary')
.where({ thread_id: threadId })
.leftJoin('thread', 'thread.id', 'thread_response.thread_id');
@@ -41,7 +41,6 @@ export interface AskingTask {
status: AskResultStatus;
candidates: Array<{
sql: string;
summary: string;
}>;
error: WrenAIError | null;
}
@@ -50,7 +49,6 @@ export interface AskingTask {
export interface DetailedThread {
id: number; // ID
sql: string; // SQL
summary: string; // Thread summary
responses: ThreadResponse[];
}
@@ -207,7 +205,6 @@ export class AskingResolver {
return {
type: response.type,
sql: response.sql,
summary: response.summary,
view,
};
}),
@@ -227,7 +224,6 @@ export class AskingResolver {
data: {
question?: string;
sql?: string;
summary?: string;
viewId?: number;
};
},
@@ -272,20 +268,12 @@ export class AskingResolver {
if (!acc.id) {
acc.id = response.threadId;
acc.sql = response.sql;
acc.summary = response.threadSummary;
acc.responses = [];
}
acc.responses.push({
id: response.id,
question: response.question,
// we added summary in version 0.3.0.
// if summary is not available, we use description and question instead.
summary:
response.summary ||
response.detail?.description ||
response.question,
status: response.status,
detail: response.detail,
error: response.error,
@@ -357,7 +345,6 @@ export class AskingResolver {
data: {
question?: string;
sql?: string;
summary?: string;
viewId?: number;
};
},
@@ -395,13 +382,7 @@ export class AskingResolver {
const askingService = ctx.askingService;
const response = await askingService.getResponse(responseId);
// we added summary in version 0.3.0.
// if summary is not available, we use description and question instead.
return {
...response,
summary:
response.summary || response.detail?.description || response.question,
};
return response;
}
public async previewData(
@@ -842,7 +842,6 @@ export class ModelResolver {
// properties from the thread response
responseId, // helpful for mapping back to the thread response
question: response.question,
summary: response.summary,
// detail is not going to send to AI service for indexing, but useful if we want display on UI
detail: response.detail,
};
+1 -5
View File
@@ -537,6 +537,7 @@ export const typeDefs = gql`
UNDERSTANDING
SEARCHING
GENERATING
CORRECTING
FINISHED
FAILED
STOPPED
@@ -556,7 +557,6 @@ export const typeDefs = gql`
type ResultCandidate {
type: ResultCandidateType!
sql: String!
summary: String!
view: ViewInfo
}
@@ -594,14 +594,12 @@ export const typeDefs = gql`
input CreateThreadInput {
question: String
sql: String
summary: String
viewId: Int
}
input CreateThreadResponseInput {
question: String
sql: String
summary: String
viewId: Int
}
@@ -638,7 +636,6 @@ export const typeDefs = gql`
type ThreadResponse {
id: Int!
question: String!
summary: String!
status: AskingTaskStatus!
detail: ThreadResponseDetail
error: Error
@@ -661,7 +658,6 @@ export const typeDefs = gql`
@deprecated(
reason: "Doesn't seem to be reasonable to put a sql in a thread"
)
summary: String!
responses: [ThreadResponse!]!
}
@@ -51,10 +51,13 @@ export interface AskingTaskInput {
export interface AskingDetailTaskInput {
question?: string;
sql?: string;
summary?: string;
viewId?: number;
}
export interface AskingDetailTaskUpdateInput {
summary?: string;
}
export enum RecommendQuestionResultStatus {
NOT_STARTED = 'NOT_STARTED',
GENERATING = 'GENERATING',
@@ -92,7 +95,7 @@ export interface IAskingService {
): Promise<Thread>;
updateThread(
threadId: number,
input: Partial<AskingDetailTaskInput>,
input: Partial<AskingDetailTaskUpdateInput>,
): Promise<Thread>;
deleteThread(threadId: number): Promise<void>;
listThreads(): Promise<Thread[]>;
@@ -460,7 +463,7 @@ export class AskingService implements IAskingService {
const deployId = await this.getDeployId();
// if it's a follow-up question, then the input will have a threadId
// then use the threadId to get the sql, summary and get the steps of last thread response
// then use the threadId to get the sql and get the steps of last thread response
// construct it into AskHistory and pass to ask
const history: AskHistory = threadId
? await this.getHistory(threadId)
@@ -514,7 +517,6 @@ export class AskingService implements IAskingService {
const response = await this.wrenAIAdaptor.generateAskDetail({
query: input.question,
sql: input.sql,
summary: input.summary,
configurations: { language },
});
@@ -523,15 +525,13 @@ export class AskingService implements IAskingService {
const thread = await this.threadRepository.createOne({
projectId: id,
sql: input.sql,
summary: input.summary,
summary: input.question,
});
// in follow-up questions, we still need to save the summary
const threadResponse = await this.threadResponseRepository.createOne({
threadId: thread.id,
queryId: response.queryId,
question: input.question,
summary: input.summary,
status: AskResultStatus.UNDERSTANDING,
});
@@ -549,7 +549,7 @@ export class AskingService implements IAskingService {
public async updateThread(
threadId: number,
input: Partial<AskingDetailTaskInput>,
input: Partial<AskingDetailTaskUpdateInput>,
): Promise<Thread> {
// if input is empty, throw error
if (isEmpty(input)) {
@@ -594,18 +594,15 @@ export class AskingService implements IAskingService {
const response = await this.wrenAIAdaptor.generateAskDetail({
query: input.question,
sql: input.sql,
summary: input.summary,
configurations: { language },
});
// 2. create a thread and the first thread response
// in follow-up questions, we still need to save the summary
// insert question first, another API all the asked questions to generate the next recommendation questions
const threadResponse = await this.threadResponseRepository.createOne({
threadId: thread.id,
queryId: response.queryId,
question: input.question,
summary: input.summary,
status: AskResultStatus.UNDERSTANDING,
});
@@ -718,7 +715,6 @@ export class AskingService implements IAskingService {
const latestResponse = responses[0];
return {
sql: latestResponse.sql,
summary: latestResponse.summary,
steps: latestResponse.detail.steps,
};
}
@@ -747,7 +743,6 @@ export class AskingService implements IAskingService {
threadId: thread.id,
queryId: QUERY_ID_PLACEHOLDER,
question: properties.question,
summary: properties.summary,
status: AskResultStatus.FINISHED,
detail: {
...properties.detail,
@@ -41,7 +41,7 @@ interface Props {
recommendedQuestions: RecommendedQuestionsTask;
};
error?: any;
onSelectResult: (payload: { sql: string; summary: string }) => void;
onSelectResult: (payload: { sql: string }) => void;
onSelectQuestion: (question: string) => void;
onClose: () => void;
onStop: () => void;
@@ -160,8 +160,7 @@ const Finished = (props: Props) => {
useEffect(() => {
if (candidates.length) {
const [result] = candidates;
onSelectResult &&
onSelectResult({ sql: result.sql, summary: result.summary });
onSelectResult && onSelectResult({ sql: result.sql });
}
}, [data]);
@@ -23,7 +23,6 @@ import {
interface Props {
onSelect: (payload: {
sql?: string;
summary?: string;
question?: string;
viewId?: number;
}) => Promise<void>;
@@ -63,6 +62,8 @@ const convertAskingTaskToProcessState = (data: AskingTask) => {
[AskingTaskStatus.UNDERSTANDING]: PROCESS_STATE.UNDERSTANDING,
[AskingTaskStatus.SEARCHING]: PROCESS_STATE.SEARCHING,
[AskingTaskStatus.GENERATING]: PROCESS_STATE.GENERATING,
// Show generating state component when AI correcting
[AskingTaskStatus.CORRECTING]: PROCESS_STATE.GENERATING,
[AskingTaskStatus.FINISHED]: PROCESS_STATE.FINISHED,
}[data.status];
@@ -147,7 +148,6 @@ export default forwardRef<Attributes, Props>(function Prompt(props, ref) {
} else if (question) {
data = {
sql: payload.sql,
summary: payload.summary,
question,
};
}
@@ -1,13 +1,15 @@
import { useState } from 'react';
import clsx from 'clsx';
import Link from 'next/link';
import { Col, Button, Row, Skeleton, Typography } from 'antd';
import { Button, Skeleton, Typography, Alert } from 'antd';
import styled from 'styled-components';
import { Path } from '@/utils/enum';
import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
import QuestionCircleOutlined from '@ant-design/icons/QuestionCircleOutlined';
import MessageOutlined from '@ant-design/icons/MessageOutlined';
import SaveOutlined from '@ant-design/icons/SaveOutlined';
import FileDoneOutlined from '@ant-design/icons/FileDoneOutlined';
import StepContent from '@/components/pages/home/promptThread/StepContent';
import { getIsFinished } from '@/hooks/useAskPrompt';
import { ThreadResponse } from '@/apollo/client/graphql/__types__';
const { Title, Text } = Typography;
@@ -25,20 +27,6 @@ const StyledAnswer = styled(Typography)`
}
`;
const StyledQuestion = styled(Row)`
padding: 4px 8px;
border-radius: 4px;
color: var(--gray-6);
background-color: var(--gray-3);
margin-bottom: 8px;
font-size: 14px;
&:hover {
background-color: var(--gray-4) !important;
cursor: pointer;
}
`;
const StyledSkeleton = styled(Skeleton)`
.ant-skeleton-title {
margin-top: 0;
@@ -47,116 +35,111 @@ const StyledSkeleton = styled(Skeleton)`
interface Props {
motion: boolean;
loading: boolean;
question: string;
description: string;
answerResultSteps: Array<{
summary: string;
sql: string;
}>;
fullSql: string;
threadResponseId: number;
threadResponse: ThreadResponse;
isLastThreadResponse: boolean;
summary: string;
view?: {
id: number;
displayName: string;
};
onOpenSaveAsViewModal: (data: { sql: string; responseId: number }) => void;
onInitPreviewDone: () => void;
}
const QuestionTitle = (props) => {
const { question, className } = props;
return (
<Title
className={clsx('d-flex bg-gray-1 rounded mt-0', className)}
level={4}
>
<MessageOutlined className="geekblue-5 mt-1 mr-3" />
<Text className="text-medium gray-8">{question}</Text>
</Title>
);
};
export default function AnswerResult(props: Props) {
const {
motion,
loading,
question,
description,
answerResultSteps,
fullSql,
threadResponseId,
threadResponse,
isLastThreadResponse,
onOpenSaveAsViewModal,
onInitPreviewDone,
summary,
view,
} = props;
const { question, status, error } = threadResponse;
const { steps, description, view, sql } = threadResponse?.detail || {};
const loading = !getIsFinished(status);
const isViewSaved = !!view;
const [ellipsis, setEllipsis] = useState(true);
const resultStyle = isLastThreadResponse
? { minHeight: 'calc(100vh - (194px))' }
: null;
return (
<div style={resultStyle}>
<StyledQuestion wrap={false} onClick={() => setEllipsis(!ellipsis)}>
<Col className="text-center" flex="96px">
<QuestionCircleOutlined className="mr-2 gray-6" />
<Text className="gray-6 text-base text-medium">Question:</Text>
</Col>
<Col flex="auto">
<Text className="gray-6" ellipsis={ellipsis}>
{question}
</Text>
</Col>
</StyledQuestion>
<Title className="mb-6 text-bold gray-10" level={3}>
{summary}
</Title>
<StyledSkeleton active loading={loading}>
<div className={motion ? 'promptThread-answer' : ''}>
<StyledAnswer className="text-md gray-10 p-3 pr-10 pt-6">
<Text className="adm-answer-title px-2">
<CheckCircleFilled className="mr-2 green-6" />
Summary
</Text>
<div className="pl-7 pb-5">{description}</div>
{(answerResultSteps || []).map((step, index) => (
<StepContent
isLastStep={index === answerResultSteps.length - 1}
key={`${step.summary}-${index}`}
sql={step.sql}
fullSql={fullSql}
stepIndex={index}
summary={step.summary}
threadResponseId={threadResponseId}
onInitPreviewDone={onInitPreviewDone}
isLastThreadResponse={isLastThreadResponse}
/>
))}
</StyledAnswer>
{isViewSaved ? (
<div className="mt-2 gray-6 text-medium">
<FileDoneOutlined className="mr-2" />
Generated from saved view{' '}
<Link
className="gray-7"
href={`${Path.Modeling}?viewId=${view.id}&openMetadata=true`}
target="_blank"
rel="noreferrer noopener"
<QuestionTitle className="mb-9" question={question} />
{error ? (
<Alert
message={error.shortMessage}
description={error.message}
type="error"
showIcon
/>
) : (
<StyledSkeleton active loading={loading}>
<div className={clsx({ 'promptThread-answer': motion })}>
<StyledAnswer className="text-md gray-10 p-3 pr-10 pt-6">
<Text className="adm-answer-title px-2">
<CheckCircleFilled className="mr-2 green-6" />
Summary
</Text>
<div className="pl-7 pb-5">{description}</div>
{(steps || []).map((step, index) => (
<StepContent
isLastStep={index === steps.length - 1}
key={`${step.summary}-${index}`}
sql={step.sql}
fullSql={sql}
stepIndex={index}
summary={step.summary}
threadResponseId={threadResponse.id}
onInitPreviewDone={onInitPreviewDone}
isLastThreadResponse={isLastThreadResponse}
/>
))}
</StyledAnswer>
{isViewSaved ? (
<div className="mt-2 gray-6 text-medium">
<FileDoneOutlined className="mr-2" />
Generated from saved view{' '}
<Link
className="gray-7"
href={`${Path.Modeling}?viewId=${view.id}&openMetadata=true`}
target="_blank"
rel="noreferrer noopener"
>
{view.displayName}
</Link>
</div>
) : (
<Button
className="mt-2 gray-6"
type="text"
size="small"
icon={<SaveOutlined />}
onClick={() =>
onOpenSaveAsViewModal({
sql,
responseId: threadResponse.id,
})
}
>
{view.displayName}
</Link>
</div>
) : (
<Button
className="mt-2 gray-6"
type="text"
size="small"
icon={<SaveOutlined />}
onClick={() =>
onOpenSaveAsViewModal({
sql: fullSql,
responseId: threadResponseId,
})
}
>
Save as View
</Button>
)}
</div>
</StyledSkeleton>
Save as View
</Button>
)}
</div>
</StyledSkeleton>
)}
</div>
);
}
@@ -1,12 +1,12 @@
import { useRouter } from 'next/router';
import { useEffect, useMemo, useRef } from 'react';
import { Alert, Divider } from 'antd';
import { Divider } from 'antd';
import styled from 'styled-components';
import AnswerResult from './AnswerResult';
import { makeIterable } from '@/utils/iteration';
import { makeIterable, IterableComponent } from '@/utils/iteration';
import {
AskingTaskStatus,
DetailedThread,
ThreadResponse,
} from '@/apollo/client/graphql/__types__';
import { getIsFinished } from '@/hooks/useAskPrompt';
@@ -39,49 +39,34 @@ const StyledPromptThread = styled.div`
}
`;
const AnswerResultTemplate = ({
id,
const AnswerResultTemplate: React.FC<
IterableComponent<ThreadResponse> & {
motion: boolean;
onOpenSaveAsViewModal: (data: { sql: string; responseId: number }) => void;
onInitPreviewDone: () => void;
}
> = ({
data,
index,
error,
status,
detail,
summary,
question,
motion,
// callbacks
onOpenSaveAsViewModal,
onInitPreviewDone,
...threadResponse
}) => {
const { id } = threadResponse;
const lastResponseId = data[data.length - 1].id;
const isLastThreadResponse = id === lastResponseId;
return (
<div key={`${id}-${index}`}>
{index > 0 && <Divider />}
{error ? (
<Alert
message={error.shortMessage}
description={error.message}
type="error"
showIcon
/>
) : (
<AnswerResult
motion={motion}
answerResultSteps={detail?.steps}
description={detail?.description}
loading={status !== AskingTaskStatus.FINISHED}
question={question}
summary={summary}
view={detail?.view}
fullSql={detail?.sql}
threadResponseId={id}
onOpenSaveAsViewModal={onOpenSaveAsViewModal}
onInitPreviewDone={onInitPreviewDone}
isLastThreadResponse={isLastThreadResponse}
/>
)}
<AnswerResult
motion={motion}
isLastThreadResponse={isLastThreadResponse}
onOpenSaveAsViewModal={onOpenSaveAsViewModal}
onInitPreviewDone={onInitPreviewDone}
threadResponse={threadResponse}
/>
</div>
);
};
+2 -1
View File
@@ -150,7 +150,8 @@ export default function useAskPrompt(threadId?: number) {
const onStopRecommend = () => instantRecommendedQuestionsResult.stopPolling();
const onStoreThreadQuestions = (questions: string[]) => setThreadQuestions(questions);
const onStoreThreadQuestions = (questions: string[]) =>
setThreadQuestions(questions);
return {
data,
+2 -2
View File
@@ -94,8 +94,8 @@ export default function HomeThread() {
}
// store thread questions for instant recommended questions
const questions = thread?.responses.flatMap((res) => res.question || [])
if(questions) askPrompt.onStoreThreadQuestions(questions);
const questions = thread?.responses.flatMap((res) => res.question || []);
if (questions) askPrompt.onStoreThreadQuestions(questions);
}, [thread]);
useEffect(() => {