From 30e878508c7f3a00d2290538936f67d2465ce907 Mon Sep 17 00:00:00 2001 From: Chih-Yu Yeh Date: Fri, 28 Mar 2025 11:26:22 +0800 Subject: [PATCH 01/22] fix(wren-ai-service): fix global instructions indexing issue (#1480) --- .../src/pipelines/indexing/instructions.py | 4 ++- .../src/web/v1/services/instructions.py | 31 +++++++++++++------ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/wren-ai-service/src/pipelines/indexing/instructions.py b/wren-ai-service/src/pipelines/indexing/instructions.py index bcb417f675..4c376f2310 100644 --- a/wren-ai-service/src/pipelines/indexing/instructions.py +++ b/wren-ai-service/src/pipelines/indexing/instructions.py @@ -43,7 +43,9 @@ def run(self, instructions: list[Instruction], project_id: Optional[str] = ""): "is_default": instruction.is_default, **addition, }, - content=instruction.question, + content="this is a global instruction, so no question is provided" + if instruction.is_default + else instruction.question, ) for instruction in instructions ] diff --git a/wren-ai-service/src/web/v1/services/instructions.py b/wren-ai-service/src/web/v1/services/instructions.py index 393175f03f..cb6d17442d 100644 --- a/wren-ai-service/src/web/v1/services/instructions.py +++ b/wren-ai-service/src/web/v1/services/instructions.py @@ -74,16 +74,27 @@ async def index( trace_id = kwargs.get("trace_id") try: - instructions = [ - Instruction( - id=instruction.id, - instruction=instruction.instruction, - question=question, - is_default=instruction.is_default, - ) - for instruction in request.instructions - for question in instruction.questions - ] + instructions = [] + for instruction in request.instructions: + if instruction.is_default: + instructions.append( + Instruction( + id=instruction.id, + instruction=instruction.instruction, + question="", + is_default=True, + ) + ) + else: + for question in instruction.questions: + instructions.append( + Instruction( + id=instruction.id, + instruction=instruction.instruction, + question=question, + is_default=False, + ) + ) await self._pipelines["instructions_indexing"].run( project_id=request.project_id, From bd9f9b177f288ac240fe264753cde12fed132e0c Mon Sep 17 00:00:00 2001 From: "wren-ai[bot]" Date: Fri, 28 Mar 2025 03:27:21 +0000 Subject: [PATCH 02/22] Upgrade AI Service version to 0.18.1 --- wren-ai-service/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wren-ai-service/pyproject.toml b/wren-ai-service/pyproject.toml index 68406cad3e..371222dc04 100644 --- a/wren-ai-service/pyproject.toml +++ b/wren-ai-service/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wren-ai-service" -version = "0.18.0" +version = "0.18.1" description = "" authors = ["dev@getwren.ai"] license = "AGPL-3.0" From ec6abfe24ad822b60b0f81fa9101894a38677052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?william=20chang=28=E5=BC=B5=E4=BB=B2=E5=A8=81=29?= Date: Fri, 28 Mar 2025 13:25:26 +0800 Subject: [PATCH 03/22] Release 0.18.0-rc.2 (ai-env-changed) (#1481) --- docker/.env.example | 4 ++-- wren-launcher/utils/docker.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/.env.example b/docker/.env.example index 7cc5971dbd..f8027e7572 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -20,9 +20,9 @@ OPENAI_API_KEY= # version # CHANGE THIS TO THE LATEST VERSION -WREN_PRODUCT_VERSION=0.18.0-rc.1 +WREN_PRODUCT_VERSION=0.18.0-rc.2 WREN_ENGINE_VERSION=0.14.8 -WREN_AI_SERVICE_VERSION=0.18.0 +WREN_AI_SERVICE_VERSION=0.18.1 IBIS_SERVER_VERSION=0.14.8 WREN_UI_VERSION=0.23.2 WREN_BOOTSTRAP_VERSION=0.1.5 diff --git a/wren-launcher/utils/docker.go b/wren-launcher/utils/docker.go index 3fbdc08310..b262ef9706 100644 --- a/wren-launcher/utils/docker.go +++ b/wren-launcher/utils/docker.go @@ -24,7 +24,7 @@ import ( const ( // please change the version when the version is updated - WREN_PRODUCT_VERSION string = "0.18.0-rc.1" + WREN_PRODUCT_VERSION string = "0.18.0-rc.2" DOCKER_COMPOSE_YAML_URL string = "https://raw.githubusercontent.com/Canner/WrenAI/" + WREN_PRODUCT_VERSION + "/docker/docker-compose.yaml" DOCKER_COMPOSE_ENV_URL string = "https://raw.githubusercontent.com/Canner/WrenAI/" + WREN_PRODUCT_VERSION + "/docker/.env.example" AI_SERVICE_CONFIG_URL string = "https://raw.githubusercontent.com/Canner/WrenAI/" + WREN_PRODUCT_VERSION + "/docker/config.example.yaml" From fe6d1ad7c3b6dc60841e525bbf6645975c51d0c7 Mon Sep 17 00:00:00 2001 From: Andy Yen <38731840+onlyjackfrost@users.noreply.github.com> Date: Fri, 28 Mar 2025 14:42:02 +0800 Subject: [PATCH 04/22] fix(wren-ui): correct generate instruction request payload (#1482) --- .../apollo/server/adaptors/wrenAIAdaptor.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/wren-ui/src/apollo/server/adaptors/wrenAIAdaptor.ts b/wren-ui/src/apollo/server/adaptors/wrenAIAdaptor.ts index f81449223e..44810a503f 100644 --- a/wren-ui/src/apollo/server/adaptors/wrenAIAdaptor.ts +++ b/wren-ui/src/apollo/server/adaptors/wrenAIAdaptor.ts @@ -542,19 +542,19 @@ export class WrenAIAdaptor implements IWrenAIAdaptor { public async generateInstruction( input: GenerateInstructionInput[], ): Promise { - const body = input.map((item) => ({ - id: item.id.toString(), - instruction: item.instruction, - questions: item.questions, - is_default: item.isDefault, - project_id: item.projectId?.toString(), - })); + const body = { + instructions: input.map((item) => ({ + id: item.id.toString(), + instruction: item.instruction, + questions: item.questions, + is_default: item.isDefault, + })), + project_id: input[0]?.projectId.toString(), + }; try { const res = await axios.post( `${this.wrenAIBaseEndpoint}/v1/instructions`, - { - instructions: body, - }, + body, ); return { queryId: res.data.event_id }; } catch (err: any) { From ab5cb105db323912762d10ac1a063218811a358f Mon Sep 17 00:00:00 2001 From: "wren-ai[bot]" Date: Fri, 28 Mar 2025 06:53:00 +0000 Subject: [PATCH 05/22] update wren-ui version to 0.23.3 --- wren-ui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wren-ui/package.json b/wren-ui/package.json index 48893af357..6382387cde 100644 --- a/wren-ui/package.json +++ b/wren-ui/package.json @@ -1,6 +1,6 @@ { "name": "wren-ui", - "version": "0.23.2", + "version": "0.23.3", "private": true, "scripts": { "dev": "next dev", From 8a6c8b4d1a8e47cecf22cf5c67196d8e313ab2ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?william=20chang=28=E5=BC=B5=E4=BB=B2=E5=A8=81=29?= Date: Fri, 28 Mar 2025 14:53:36 +0800 Subject: [PATCH 06/22] release 0.18.0-rc.3 (ai-env-changed) (#1483) --- docker/.env.example | 4 ++-- wren-launcher/utils/docker.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/.env.example b/docker/.env.example index f8027e7572..313753c3f8 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -20,11 +20,11 @@ OPENAI_API_KEY= # version # CHANGE THIS TO THE LATEST VERSION -WREN_PRODUCT_VERSION=0.18.0-rc.2 +WREN_PRODUCT_VERSION=0.18.0-rc.3 WREN_ENGINE_VERSION=0.14.8 WREN_AI_SERVICE_VERSION=0.18.1 IBIS_SERVER_VERSION=0.14.8 -WREN_UI_VERSION=0.23.2 +WREN_UI_VERSION=0.23.3 WREN_BOOTSTRAP_VERSION=0.1.5 # user id (uuid v4) diff --git a/wren-launcher/utils/docker.go b/wren-launcher/utils/docker.go index b262ef9706..395315cde4 100644 --- a/wren-launcher/utils/docker.go +++ b/wren-launcher/utils/docker.go @@ -24,7 +24,7 @@ import ( const ( // please change the version when the version is updated - WREN_PRODUCT_VERSION string = "0.18.0-rc.2" + WREN_PRODUCT_VERSION string = "0.18.0-rc.3" DOCKER_COMPOSE_YAML_URL string = "https://raw.githubusercontent.com/Canner/WrenAI/" + WREN_PRODUCT_VERSION + "/docker/docker-compose.yaml" DOCKER_COMPOSE_ENV_URL string = "https://raw.githubusercontent.com/Canner/WrenAI/" + WREN_PRODUCT_VERSION + "/docker/.env.example" AI_SERVICE_CONFIG_URL string = "https://raw.githubusercontent.com/Canner/WrenAI/" + WREN_PRODUCT_VERSION + "/docker/config.example.yaml" From c9f3379a9b5b5dddaf1b7b04aff511dca3864f8f Mon Sep 17 00:00:00 2001 From: Andy Yen <38731840+onlyjackfrost@users.noreply.github.com> Date: Fri, 28 Mar 2025 15:11:35 +0800 Subject: [PATCH 07/22] CI: add latest tag to ui image (#1485) --- .github/workflows/ui-release-image-stable.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ui-release-image-stable.yaml b/.github/workflows/ui-release-image-stable.yaml index f22014f38d..0aff2fcc86 100644 --- a/.github/workflows/ui-release-image-stable.yaml +++ b/.github/workflows/ui-release-image-stable.yaml @@ -87,8 +87,9 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Create manifest list and push working-directory: /tmp/digests + # tag with input version and latest run: | - TAGS=$(echo "${{ steps.meta.outputs.tags }}" | awk '{printf "--tag %s ", $0}') + TAGS=$(echo "${{ steps.meta.outputs.tags }}" | awk '{printf "--tag %s ", $0}') --tag latest docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ $(printf '${{ env.WREN_UI_IMAGE }}@sha256:%s ' *) \ $TAGS From 563aa470ee7886e66c3ecf3d4d2679bd5abeee92 Mon Sep 17 00:00:00 2001 From: Chih-Yu Yeh Date: Fri, 28 Mar 2025 17:34:14 +0800 Subject: [PATCH 08/22] fix(wren-ai-service): delete instructions (#1489) --- wren-ai-service/src/pipelines/indexing/instructions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wren-ai-service/src/pipelines/indexing/instructions.py b/wren-ai-service/src/pipelines/indexing/instructions.py index 4c376f2310..83a3f70707 100644 --- a/wren-ai-service/src/pipelines/indexing/instructions.py +++ b/wren-ai-service/src/pipelines/indexing/instructions.py @@ -166,12 +166,12 @@ async def run( @observe(name="Clean Documents for Instructions") async def clean( self, - instructions: List[Instruction], + instructions: Optional[List[Instruction]] = None, project_id: Optional[str] = None, delete_all: bool = False, ) -> None: await clean( - instructions=instructions, + instructions=instructions or [], cleaner=self._components["cleaner"], project_id=project_id, delete_all=delete_all, From 528209a5470a4a9c086a8c87e55d7893be3afed9 Mon Sep 17 00:00:00 2001 From: "wren-ai[bot]" Date: Fri, 28 Mar 2025 09:34:59 +0000 Subject: [PATCH 09/22] Upgrade AI Service version to 0.18.2 --- wren-ai-service/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wren-ai-service/pyproject.toml b/wren-ai-service/pyproject.toml index 371222dc04..d37693c88d 100644 --- a/wren-ai-service/pyproject.toml +++ b/wren-ai-service/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wren-ai-service" -version = "0.18.1" +version = "0.18.2" description = "" authors = ["dev@getwren.ai"] license = "AGPL-3.0" From c7b38d5630fbc7f098269e605551d56a2b0feff1 Mon Sep 17 00:00:00 2001 From: Shimin Date: Fri, 28 Mar 2025 17:56:49 +0800 Subject: [PATCH 10/22] fix(wren-ui): separate instant recommend questions to new effect (#1488) --- wren-ui/src/hooks/useAskPrompt.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wren-ui/src/hooks/useAskPrompt.tsx b/wren-ui/src/hooks/useAskPrompt.tsx index 3f5c2b0f67..e9f242874b 100644 --- a/wren-ui/src/hooks/useAskPrompt.tsx +++ b/wren-ui/src/hooks/useAskPrompt.tsx @@ -228,12 +228,14 @@ export default function useAskPrompt(threadId?: number) { checkFetchAskingStreamTask(askingTask); } } + }, [askingTask?.status, threadId, checkFetchAskingStreamTask]); + useEffect(() => { // handle instant recommended questions if (isNeedRecommendedQuestions(askingTask)) { startRecommendedQuestions(); } - }, [askingTask, threadId, checkFetchAskingStreamTask]); + }, [askingTask?.type]); useEffect(() => { if (isRecommendedFinished(recommendedQuestions?.status)) From 61995e6974e21cfd0785c2071458226ee90a1597 Mon Sep 17 00:00:00 2001 From: Andy Yen <38731840+onlyjackfrost@users.noreply.github.com> Date: Fri, 28 Mar 2025 18:16:53 +0800 Subject: [PATCH 11/22] fix(wren-ui): delete semantics when reseting project (#1490) --- .gitignore | 1 + .../apollo/server/adaptors/wrenAIAdaptor.ts | 26 +++++++++++++++++++ .../server/resolvers/projectResolver.ts | 1 + 3 files changed, 28 insertions(+) diff --git a/.gitignore b/.gitignore index f62facef6c..741105ee4c 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ wren-ai-service/tools/dev/etc/** .deepeval-cache.json .deepeval_telemtry.txt docker/config.yaml +docker/docker-compose-local.yaml # python .python-version diff --git a/wren-ui/src/apollo/server/adaptors/wrenAIAdaptor.ts b/wren-ui/src/apollo/server/adaptors/wrenAIAdaptor.ts index 44810a503f..453ace9678 100644 --- a/wren-ui/src/apollo/server/adaptors/wrenAIAdaptor.ts +++ b/wren-ui/src/apollo/server/adaptors/wrenAIAdaptor.ts @@ -48,6 +48,7 @@ const getAIServiceError = (error: any) => { export interface IWrenAIAdaptor { deploy(deployData: DeployData): Promise; + delete(projectId: number): Promise; /** * Ask AI service a question. @@ -126,6 +127,31 @@ export class WrenAIAdaptor implements IWrenAIAdaptor { constructor({ wrenAIBaseEndpoint }: { wrenAIBaseEndpoint: string }) { this.wrenAIBaseEndpoint = wrenAIBaseEndpoint; } + + public async delete(projectId: number): Promise { + try { + if (!projectId) { + throw new Error('Project ID is required'); + } + const url = `${this.wrenAIBaseEndpoint}/v1/semantics`; + const response = await axios.delete(url, { + params: { + project_id: projectId.toString(), + }, + }); + + if (response.status === 200) { + logger.info(`Wren AI: Deleted semantics for project ${projectId}`); + } else { + throw new Error(`Failed to delete semantics. ${response.data?.error}`); + } + } catch (error: any) { + throw new Error( + `Wren AI: Failed to delete semantics: ${getAIServiceError(error)}`, + ); + } + } + public async deploySqlPair( projectId: number, sqlPair: Partial, diff --git a/wren-ui/src/apollo/server/resolvers/projectResolver.ts b/wren-ui/src/apollo/server/resolvers/projectResolver.ts index e07bdb6a8e..c8e6022989 100644 --- a/wren-ui/src/apollo/server/resolvers/projectResolver.ts +++ b/wren-ui/src/apollo/server/resolvers/projectResolver.ts @@ -130,6 +130,7 @@ export class ProjectResolver { await ctx.modelService.deleteAllViewsByProjectId(id); await ctx.modelService.deleteAllModelsByProjectId(id); await ctx.projectService.deleteProject(id); + await ctx.wrenAIAdaptor.delete(id); // telemetry ctx.telemetry.sendEvent(eventName, { From 22dd812e88131b34c5db4035d917a9635b527f73 Mon Sep 17 00:00:00 2001 From: Andy Yen <38731840+onlyjackfrost@users.noreply.github.com> Date: Fri, 28 Mar 2025 18:17:46 +0800 Subject: [PATCH 12/22] chore(workflow): fix latest tag (#1491) --- .github/workflows/ui-release-image-stable.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ui-release-image-stable.yaml b/.github/workflows/ui-release-image-stable.yaml index 0aff2fcc86..eebcc50bf3 100644 --- a/.github/workflows/ui-release-image-stable.yaml +++ b/.github/workflows/ui-release-image-stable.yaml @@ -89,7 +89,7 @@ jobs: working-directory: /tmp/digests # tag with input version and latest run: | - TAGS=$(echo "${{ steps.meta.outputs.tags }}" | awk '{printf "--tag %s ", $0}') --tag latest + TAGS=$(echo "${{ steps.meta.outputs.tags }} --tag latest" | awk '{printf "--tag %s ", $0}') docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ $(printf '${{ env.WREN_UI_IMAGE }}@sha256:%s ' *) \ $TAGS From 2457cadcfa5b3e90965f05b55df4152736fd8ad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?william=20chang=28=E5=BC=B5=E4=BB=B2=E5=A8=81=29?= Date: Fri, 28 Mar 2025 18:41:34 +0800 Subject: [PATCH 13/22] Release 0.18.0-rc.4 (ai-env-changed) (#1492) --- docker/.env.example | 6 +++--- wren-launcher/utils/docker.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/.env.example b/docker/.env.example index 313753c3f8..0cd4e34444 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -20,11 +20,11 @@ OPENAI_API_KEY= # version # CHANGE THIS TO THE LATEST VERSION -WREN_PRODUCT_VERSION=0.18.0-rc.3 +WREN_PRODUCT_VERSION=0.18.0-rc.4 WREN_ENGINE_VERSION=0.14.8 -WREN_AI_SERVICE_VERSION=0.18.1 +WREN_AI_SERVICE_VERSION=0.18.2 IBIS_SERVER_VERSION=0.14.8 -WREN_UI_VERSION=0.23.3 +WREN_UI_VERSION=0.23.4 WREN_BOOTSTRAP_VERSION=0.1.5 # user id (uuid v4) diff --git a/wren-launcher/utils/docker.go b/wren-launcher/utils/docker.go index 395315cde4..323c717e98 100644 --- a/wren-launcher/utils/docker.go +++ b/wren-launcher/utils/docker.go @@ -24,7 +24,7 @@ import ( const ( // please change the version when the version is updated - WREN_PRODUCT_VERSION string = "0.18.0-rc.3" + WREN_PRODUCT_VERSION string = "0.18.0-rc.4" DOCKER_COMPOSE_YAML_URL string = "https://raw.githubusercontent.com/Canner/WrenAI/" + WREN_PRODUCT_VERSION + "/docker/docker-compose.yaml" DOCKER_COMPOSE_ENV_URL string = "https://raw.githubusercontent.com/Canner/WrenAI/" + WREN_PRODUCT_VERSION + "/docker/.env.example" AI_SERVICE_CONFIG_URL string = "https://raw.githubusercontent.com/Canner/WrenAI/" + WREN_PRODUCT_VERSION + "/docker/config.example.yaml" From 4422dc72279012acaaa3bc39c45da7876f3c3351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?william=20chang=28=E5=BC=B5=E4=BB=B2=E5=A8=81=29?= Date: Fri, 28 Mar 2025 19:02:35 +0800 Subject: [PATCH 14/22] release 0.18.0 (ai-env-changed) (#1493) --- docker/.env.example | 2 +- wren-launcher/utils/docker.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/.env.example b/docker/.env.example index 0cd4e34444..9aa4b0dc01 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -20,7 +20,7 @@ OPENAI_API_KEY= # version # CHANGE THIS TO THE LATEST VERSION -WREN_PRODUCT_VERSION=0.18.0-rc.4 +WREN_PRODUCT_VERSION=0.18.0 WREN_ENGINE_VERSION=0.14.8 WREN_AI_SERVICE_VERSION=0.18.2 IBIS_SERVER_VERSION=0.14.8 diff --git a/wren-launcher/utils/docker.go b/wren-launcher/utils/docker.go index 323c717e98..c5931b271b 100644 --- a/wren-launcher/utils/docker.go +++ b/wren-launcher/utils/docker.go @@ -24,7 +24,7 @@ import ( const ( // please change the version when the version is updated - WREN_PRODUCT_VERSION string = "0.18.0-rc.4" + WREN_PRODUCT_VERSION string = "0.18.0" DOCKER_COMPOSE_YAML_URL string = "https://raw.githubusercontent.com/Canner/WrenAI/" + WREN_PRODUCT_VERSION + "/docker/docker-compose.yaml" DOCKER_COMPOSE_ENV_URL string = "https://raw.githubusercontent.com/Canner/WrenAI/" + WREN_PRODUCT_VERSION + "/docker/.env.example" AI_SERVICE_CONFIG_URL string = "https://raw.githubusercontent.com/Canner/WrenAI/" + WREN_PRODUCT_VERSION + "/docker/config.example.yaml" From 9d4a600033f17ab64c9de3c4f3195452d47d2424 Mon Sep 17 00:00:00 2001 From: Andy Yen <38731840+onlyjackfrost@users.noreply.github.com> Date: Fri, 28 Mar 2025 20:14:48 +0800 Subject: [PATCH 15/22] chore: automate release workflow (#1487) --- ...e-rc-pr.yaml => create-rc-release-pr.yaml} | 55 ++----------- .github/workflows/create-rc-release.yaml | 81 +++++++++++++++++++ wren-launcher/Makefile | 4 +- 3 files changed, 88 insertions(+), 52 deletions(-) rename .github/workflows/{release-rc-pr.yaml => create-rc-release-pr.yaml} (53%) create mode 100644 .github/workflows/create-rc-release.yaml diff --git a/.github/workflows/release-rc-pr.yaml b/.github/workflows/create-rc-release-pr.yaml similarity index 53% rename from .github/workflows/release-rc-pr.yaml rename to .github/workflows/create-rc-release-pr.yaml index 9bdb5bee82..b1bb8b68bc 100644 --- a/.github/workflows/release-rc-pr.yaml +++ b/.github/workflows/create-rc-release-pr.yaml @@ -1,4 +1,4 @@ -name: Create Release RC +name: Create RC Release PR on: workflow_dispatch: @@ -32,12 +32,6 @@ jobs: git config --global user.name "wren-ai[bot]" git config --global user.email "dev@cannerdata.com" - - name: Create branch - run: | - BRANCH_NAME="release/${{ github.event.inputs.release_version }}" - git checkout -b $BRANCH_NAME - echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV - - name: Update docker.go run: | FILE_PATH="wren-launcher/utils/docker.go" @@ -70,51 +64,12 @@ jobs: echo "===== Git diff for changed files =====" git diff - - name: Commit changes - run: | - git add wren-launcher/utils/docker.go docker/.env.example - git commit -m "release ${{ github.event.inputs.release_version }}" || echo "No changes to commit" - git status - - - name: Push branch - run: | - git push --set-upstream origin ${{ env.BRANCH_NAME }} - echo "Branch pushed to origin/${{ env.BRANCH_NAME }}" - - - name: Install GitHub CLI - run: | - type -p curl >/dev/null || (apt update && apt install curl -y) - curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \ - && chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \ - && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null \ - && apt update \ - && apt install gh -y - - - name: Create Pull Request with GitHub CLI - run: | - # Check if branch has changes compared to main - if git diff --quiet origin/main origin/${{ env.BRANCH_NAME }}; then - echo "No changes detected between main and ${{ env.BRANCH_NAME }}. Cannot create PR." - exit 1 - fi - - # Create PR using gh cli - gh pr create \ - --title "Release ${{ github.event.inputs.release_version }}" \ - --body "Release ${{ github.event.inputs.release_version }}" \ - --base main \ - --head ${{ env.BRANCH_NAME }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Fallback PR creation - if: failure() - uses: peter-evans/create-pull-request@v5 + - name: Create PR + uses: peter-evans/create-pull-request@v7 with: - token: ${{ secrets.GITHUB_TOKEN }} - commit-message: "release ${{ github.event.inputs.release_version }}" - branch: ${{ env.BRANCH_NAME }} base: main + branch: "release/${{ github.event.inputs.release_version }}" + commit-message: "release ${{ github.event.inputs.release_version }}" title: "Release ${{ github.event.inputs.release_version }}" body: "Release ${{ github.event.inputs.release_version }}" draft: false diff --git a/.github/workflows/create-rc-release.yaml b/.github/workflows/create-rc-release.yaml new file mode 100644 index 0000000000..6e59aeba3c --- /dev/null +++ b/.github/workflows/create-rc-release.yaml @@ -0,0 +1,81 @@ +name: Create RC Release + +on: + workflow_dispatch: + inputs: + release_version: + description: "Release version" + required: true + issue_comment: + types: [created] + +jobs: + release: + runs-on: macos-latest + if: ${{ github.event_name == 'issue_comment' && contains(github.event.comment.body, '/release-rc') && startsWith(github.event.issue.title, 'Release') }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: main + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: "1.23.0" + + - name: Add rocket emoji to comment + run: | + curl -X POST -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + -d '{"body": "🚀 Starting the release process!"}' \ + "https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/comments" + + - name: Parse release version from PR title + id: parse_release_version + env: + GITHUB_ISSUE_TITLE: ${{ github.event.issue.title }} + run: | + release_version=$(echo "$GITHUB_ISSUE_TITLE" | sed 's/ /\n/g' | tail -n 1) + echo "Release version: $release_version" + echo "release_version=$release_version" >> $GITHUB_OUTPUT + + - name: Build for macOS + working-directory: ./wren-launcher + run: | + mkdir -p dist + env GOARCH=amd64 GOOS=darwin CGO_ENABLED=1 go build -o dist/wren-launcher-darwin main.go + cd dist && chmod +x wren-launcher-darwin && tar zcvf wren-launcher-darwin.tar.gz wren-launcher-darwin + + - name: Build for Linux + working-directory: ./wren-launcher + run: | + mkdir -p dist + env GOARCH=amd64 GOOS=linux CGO_ENABLED=0 go build -o dist/wren-launcher-linux main.go + cd dist && chmod +x wren-launcher-linux && tar zcvf wren-launcher-linux.tar.gz wren-launcher-linux + + - name: Build for Windows + working-directory: ./wren-launcher + run: | + mkdir -p dist + env GOARCH=amd64 GOOS=windows CGO_ENABLED=0 go build -o dist/wren-launcher-windows.exe main.go + cd dist && zip wren-launcher-windows.zip wren-launcher-windows.exe + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + files: | + ./wren-launcher/dist/wren-launcher-darwin.tar.gz + ./wren-launcher/dist/wren-launcher-linux.tar.gz + ./wren-launcher/dist/wren-launcher-windows.zip + tag_name: ${{ steps.parse_release_version.outputs.release_version }} + prerelease: true + generate_release_notes: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Comment with release link + run: | + release_url="https://github.com/${{ github.repository }}/releases/tag/${{ steps.parse_release_version.outputs.release_version }}" + curl -X POST -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + -d "{\"body\": \"🚀 A new release has been created! [View Release](${release_url})\"}" \ + "https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/comments" diff --git a/wren-launcher/Makefile b/wren-launcher/Makefile index 284e0c37c9..43738b515e 100644 --- a/wren-launcher/Makefile +++ b/wren-launcher/Makefile @@ -2,8 +2,8 @@ BINARY_NAME=wren-launcher build: env GOARCH=amd64 GOOS=darwin CGO_ENABLED=1 go build -o dist/${BINARY_NAME}-darwin main.go - env GOARCH=amd64 GOOS=linux go build -o dist/${BINARY_NAME}-linux main.go - env GOARCH=amd64 GOOS=windows go build -o dist/${BINARY_NAME}-windows.exe main.go + env GOARCH=amd64 GOOS=linux CGO_ENABLED=0 go build -o dist/${BINARY_NAME}-linux main.go + env GOARCH=amd64 GOOS=windows CGO_ENABLED=0 go build -o dist/${BINARY_NAME}-windows.exe main.go cd ./dist; chmod +x ${BINARY_NAME}-darwin && chmod +x ${BINARY_NAME}-linux \ && tar zcvf ${BINARY_NAME}-darwin.tar.gz ${BINARY_NAME}-darwin \ && tar zcvf ${BINARY_NAME}-linux.tar.gz ${BINARY_NAME}-linux \ From 6964262e0a03586b54398be3a181121f4eb9c4d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?william=20chang=28=E5=BC=B5=E4=BB=B2=E5=A8=81=29?= Date: Tue, 1 Apr 2025 02:01:18 +0800 Subject: [PATCH 16/22] adjustment API --- ...00000_add_adjustment_to_thread_response.js | 24 + .../apollo/server/adaptors/wrenAIAdaptor.ts | 83 +++- .../adjustmentBackgroundTracker.ts | 451 ++++++++++++++++++ .../src/apollo/server/backgrounds/index.ts | 1 + wren-ui/src/apollo/server/models/adaptor.ts | 28 ++ .../repositories/askingTaskRepository.ts | 10 +- .../repositories/threadResponseRepository.ts | 29 ++ wren-ui/src/apollo/server/resolvers.ts | 8 + .../apollo/server/resolvers/askingResolver.ts | 111 +++++ wren-ui/src/apollo/server/schema.ts | 35 ++ .../apollo/server/services/askingService.ts | 119 ++++- .../server/services/askingTaskTracker.ts | 2 +- .../src/apollo/server/telemetry/telemetry.ts | 3 + wren-ui/src/common.ts | 1 + 14 files changed, 900 insertions(+), 5 deletions(-) create mode 100644 wren-ui/migrations/20250510000000_add_adjustment_to_thread_response.js create mode 100644 wren-ui/src/apollo/server/backgrounds/adjustmentBackgroundTracker.ts diff --git a/wren-ui/migrations/20250510000000_add_adjustment_to_thread_response.js b/wren-ui/migrations/20250510000000_add_adjustment_to_thread_response.js new file mode 100644 index 0000000000..5c06d6ddf5 --- /dev/null +++ b/wren-ui/migrations/20250510000000_add_adjustment_to_thread_response.js @@ -0,0 +1,24 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = function (knex) { + return knex.schema.alterTable('thread_response', (table) => { + table + .jsonb('adjustment') + .nullable() + .comment( + 'Adjustment data for thread response, including type and payload', + ); + }); +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function (knex) { + return knex.schema.alterTable('thread_response', (table) => { + table.dropColumn('adjustment'); + }); +}; diff --git a/wren-ui/src/apollo/server/adaptors/wrenAIAdaptor.ts b/wren-ui/src/apollo/server/adaptors/wrenAIAdaptor.ts index 453ace9678..a5f6b0731b 100644 --- a/wren-ui/src/apollo/server/adaptors/wrenAIAdaptor.ts +++ b/wren-ui/src/apollo/server/adaptors/wrenAIAdaptor.ts @@ -30,6 +30,9 @@ import { GenerateInstructionInput, InstructionStatus, InstructionResult, + AskFeedbackInput, + AskFeedbackResult, + AskFeedbackStatus, } from '@server/models/adaptor'; import { getLogger } from '@server/utils'; import * as Errors from '@server/utils/error'; @@ -119,6 +122,13 @@ export interface IWrenAIAdaptor { ): Promise; getInstructionResult(queryId: string): Promise; deleteInstructions(ids: number[], projectId: number): Promise; + + /** + * Ask feedback APIs + */ + createAskFeedback(input: AskFeedbackInput): Promise; + getAskFeedbackResult(queryId: string): Promise; + cancelAskFeedback(queryId: string): Promise; } export class WrenAIAdaptor implements IWrenAIAdaptor { @@ -647,6 +657,76 @@ export class WrenAIAdaptor implements IWrenAIAdaptor { } } + public async createAskFeedback( + input: AskFeedbackInput, + ): Promise { + try { + const body = { + tables: input.tables, + sql_generation_reasoning: input.sqlGenerationReasoning, + sql: input.sql, + configurations: input.configurations, + }; + + const res = await axios.post( + `${this.wrenAIBaseEndpoint}/v1/ask-feedbacks`, + body, + ); + return { queryId: res.data.query_id }; + } catch (err: any) { + logger.debug( + `Got error when creating ask feedback: ${getAIServiceError(err)}`, + ); + throw err; + } + } + + public async getAskFeedbackResult( + queryId: string, + ): Promise { + try { + const res = await axios.get( + `${this.wrenAIBaseEndpoint}/v1/ask-feedbacks/${queryId}`, + ); + return this.transformAskFeedbackResult(res.data); + } catch (err: any) { + logger.debug( + `Got error when getting ask feedback result: ${getAIServiceError(err)}`, + ); + throw err; + } + } + + public async cancelAskFeedback(queryId: string): Promise { + try { + await axios.patch( + `${this.wrenAIBaseEndpoint}/v1/ask-feedbacks/${queryId}`, + { + status: 'stopped', + }, + ); + } catch (err: any) { + logger.debug( + `Got error when canceling ask feedback: ${getAIServiceError(err)}`, + ); + throw err; + } + } + + private transformAskFeedbackResult(body: any): AskFeedbackResult { + const { status, error } = this.transformStatusAndError(body); + return { + status: status as AskFeedbackStatus, + error, + response: + body.response?.map((result: any) => ({ + sql: result.sql, + type: result.type.toUpperCase() as AskCandidateType, + })) || [], + traceId: body.trace_id, + }; + } + private transformChartAdjustmentInput(input: ChartAdjustmentInput) { const { query, sql, adjustmentOption, chartSchema, configurations } = input; return { @@ -794,7 +874,8 @@ export class WrenAIAdaptor implements IWrenAIAdaptor { | ChartStatus | SqlPairStatus | QuestionsStatus - | InstructionStatus; + | InstructionStatus + | AskFeedbackStatus; error?: { code: Errors.GeneralErrorCodes; message: string; diff --git a/wren-ui/src/apollo/server/backgrounds/adjustmentBackgroundTracker.ts b/wren-ui/src/apollo/server/backgrounds/adjustmentBackgroundTracker.ts new file mode 100644 index 0000000000..a5050fd2d2 --- /dev/null +++ b/wren-ui/src/apollo/server/backgrounds/adjustmentBackgroundTracker.ts @@ -0,0 +1,451 @@ +import { getLogger } from '@server/utils'; +import { + AskFeedbackInput, + AskFeedbackResult, + AskFeedbackStatus, + ProjectConfigurations, +} from '@server/models/adaptor'; +import { + AskingTask, + IAskingTaskRepository, + IThreadResponseRepository, + ThreadResponse, + ThreadResponseAdjustmentType, +} from '@server/repositories'; +import { IWrenAIAdaptor } from '../adaptors'; + +const logger = getLogger('AdjustmentTaskTracker'); +logger.level = 'debug'; + +interface TrackedTask { + queryId: string; + taskId?: number; + lastPolled: number; + result?: AskFeedbackResult; + isFinalized: boolean; + threadResponseId: number; + question: string; + originalThreadResponseId: number; +} + +export type TrackedAdjustmentResult = AskFeedbackResult & { + taskId?: number; + queryId: string; +}; + +export type CreateAdjustmentTaskInput = AskFeedbackInput & { + threadId: number; + question: string; + originalThreadResponseId: number; +}; + +export type RerunAdjustmentTaskInput = { + threadResponseId: number; + threadId: number; + projectId: number; + configurations: ProjectConfigurations; +}; + +export interface IAdjustmentBackgroundTaskTracker { + createAdjustmentTask( + input: CreateAdjustmentTaskInput, + ): Promise<{ queryId: string }>; + getAdjustmentResult(queryId: string): Promise; + getAdjustmentResultById(id: number): Promise; + cancelAdjustmentTask(queryId: string): Promise; + rerunAdjustmentTask( + input: RerunAdjustmentTaskInput, + ): Promise<{ queryId: string }>; +} + +export class AdjustmentBackgroundTaskTracker + implements IAdjustmentBackgroundTaskTracker +{ + private wrenAIAdaptor: IWrenAIAdaptor; + private askingTaskRepository: IAskingTaskRepository; + private trackedTasks: Map = new Map(); + private trackedTasksById: Map = new Map(); + private pollingInterval: number; + private memoryRetentionTime: number; + private pollingIntervalId: NodeJS.Timeout; + private runningJobs = new Set(); + private threadResponseRepository: IThreadResponseRepository; + + constructor({ + wrenAIAdaptor, + askingTaskRepository, + threadResponseRepository, + pollingInterval = 1000, // 1 second + memoryRetentionTime = 5 * 60 * 1000, // 5 minutes + }: { + wrenAIAdaptor: IWrenAIAdaptor; + askingTaskRepository: IAskingTaskRepository; + threadResponseRepository: IThreadResponseRepository; + pollingInterval?: number; + memoryRetentionTime?: number; + }) { + this.wrenAIAdaptor = wrenAIAdaptor; + this.askingTaskRepository = askingTaskRepository; + this.threadResponseRepository = threadResponseRepository; + this.pollingInterval = pollingInterval; + this.memoryRetentionTime = memoryRetentionTime; + this.startPolling(); + } + + public async createAdjustmentTask( + input: CreateAdjustmentTaskInput, + ): Promise<{ queryId: string; createdThreadResponse: ThreadResponse }> { + try { + // Call the AI service to create a task + const response = await this.wrenAIAdaptor.createAskFeedback(input); + const queryId = response.queryId; + + // create a new asking task + const createdAskingTask = await this.askingTaskRepository.createOne({ + queryId, + question: input.question, + threadId: input.threadId, + detail: { + adjustment: true, + status: AskFeedbackStatus.UNDERSTANDING, + response: [], + error: null, + }, + }); + + // create a new thread response with adjustment payload + const createdThreadResponse = + await this.threadResponseRepository.createOne({ + question: input.question, + threadId: input.threadId, + askingTaskId: createdAskingTask.id, + adjustment: { + type: ThreadResponseAdjustmentType.REASONING, + payload: { + originalThreadResponseId: input.originalThreadResponseId, + retrievedTables: input.tables, + sqlGenerationReasoning: input.sqlGenerationReasoning, + }, + }, + }); + + // bind the thread response to the asking task + // todo: it's weird that we need to update the asking task again + // find a better way to do this + await this.askingTaskRepository.updateOne(createdAskingTask.id, { + threadResponseId: createdThreadResponse.id, + }); + + // Start tracking this task + const task = { + queryId, + lastPolled: Date.now(), + isFinalized: false, + originalThreadResponseId: input.originalThreadResponseId, + threadResponseId: createdThreadResponse.id, + question: input.question, + } as TrackedTask; + this.trackedTasks.set(queryId, task); + this.trackedTasksById.set(createdThreadResponse.id, task); + + logger.info(`Created adjustment task with queryId: ${queryId}`); + return { queryId, createdThreadResponse }; + } catch (err) { + logger.error(`Failed to create adjustment task: ${err}`); + throw err; + } + } + + public async rerunAdjustmentTask( + input: RerunAdjustmentTaskInput, + ): Promise<{ queryId: string }> { + const currentThreadResponse = await this.threadResponseRepository.findOneBy( + { + id: input.threadResponseId, + }, + ); + if (!currentThreadResponse) { + throw new Error(`Thread response ${input.threadResponseId} not found`); + } + + const adjustment = currentThreadResponse.adjustment; + if (!adjustment) { + throw new Error( + `Thread response ${input.threadResponseId} has no adjustment`, + ); + } + + const originalThreadResponse = + await this.threadResponseRepository.findOneBy({ + id: adjustment.payload?.originalThreadResponseId, + }); + if (!originalThreadResponse) { + throw new Error( + `Original thread response ${adjustment.payload?.originalThreadResponseId} not found`, + ); + } + + // call createAskFeedback on AI service + const response = await this.wrenAIAdaptor.createAskFeedback({ + ...input, + tables: adjustment.payload?.retrievedTables, + sqlGenerationReasoning: adjustment.payload?.sqlGenerationReasoning, + sql: originalThreadResponse.sql, + }); + const queryId = response.queryId; + + // update asking task with new queryId + await this.askingTaskRepository.updateOne( + currentThreadResponse.askingTaskId, + { + queryId, + + // reset detail + detail: { + adjustment: true, + status: AskFeedbackStatus.UNDERSTANDING, + response: [], + error: null, + }, + }, + ); + + // schedule task + const task = { + queryId, + lastPolled: Date.now(), + isFinalized: false, + originalThreadResponseId: originalThreadResponse.id, + threadResponseId: currentThreadResponse.id, + question: originalThreadResponse.question, + } as TrackedTask; + this.trackedTasks.set(queryId, task); + this.trackedTasksById.set(currentThreadResponse.id, task); + + logger.info(`Rerun adjustment task with queryId: ${queryId}`); + return { queryId }; + } + + public async getAdjustmentResult( + queryId: string, + ): Promise { + // Check if we're tracking this task in memory + const trackedTask = this.trackedTasks.get(queryId); + + if (trackedTask && trackedTask.result) { + return { + ...trackedTask.result, + queryId, + taskId: trackedTask.taskId, + }; + } + + // If not in memory or no result yet, check the database + return this.getAdjustmentResultFromDB({ queryId }); + } + + public async getAdjustmentResultById( + id: number, + ): Promise { + const task = this.trackedTasksById.get(id); + if (task) { + return this.getAdjustmentResult(task.queryId); + } + + return this.getAdjustmentResultFromDB({ taskId: id }); + } + + public async cancelAdjustmentTask(queryId: string): Promise { + await this.wrenAIAdaptor.cancelAskFeedback(queryId); + } + + public stopPolling(): void { + if (this.pollingIntervalId) { + clearInterval(this.pollingIntervalId); + } + } + + private startPolling(): void { + this.pollingIntervalId = setInterval(() => { + this.pollTasks(); + }, this.pollingInterval); + } + + private async pollTasks(): Promise { + const now = Date.now(); + const tasksToRemove: string[] = []; + + // Create an array of job functions + const jobs = Array.from(this.trackedTasks.entries()).map( + ([queryId, task]) => + async () => { + try { + // Skip if the job is already running + if (this.runningJobs.has(queryId)) { + return; + } + + // Skip finalized tasks that have been in memory too long + if ( + task.isFinalized && + now - task.lastPolled > this.memoryRetentionTime + ) { + tasksToRemove.push(queryId); + return; + } + + // Skip finalized tasks + if (task.isFinalized) { + return; + } + + // Mark the job as running + this.runningJobs.add(queryId); + + // Poll for updates + logger.info(`Polling for updates for task ${queryId}`); + const result = + await this.wrenAIAdaptor.getAskFeedbackResult(queryId); + task.lastPolled = now; + + // if result is not changed, we don't need to update the database + if (!this.isResultChanged(task.result, result)) { + this.runningJobs.delete(queryId); + return; + } + + // update task in memory if any change + task.result = result; + + // update the database + logger.info(`Updating task ${queryId} in database`); + await this.updateTaskInDatabase({ queryId }, task); + + // Check if task is now finalized + if (this.isTaskFinalized(result.status)) { + task.isFinalized = true; + // update thread response if threadResponseId is provided + if (task.threadResponseId) { + await this.updateThreadResponseWhenTaskFinalized(task); + } + + logger.info( + `Task ${queryId} is finalized with status: ${result.status}`, + ); + } + + // Mark the job as finished + this.runningJobs.delete(queryId); + } catch (err) { + this.runningJobs.delete(queryId); + logger.error(err.stack); + throw err; + } + }, + ); + + // Run all jobs in parallel + Promise.allSettled(jobs.map((job) => job())).then((results) => { + // Log any rejected promises + results.forEach((result, index) => { + if (result.status === 'rejected') { + logger.error(`Job ${index} failed: ${result.reason}`); + } + }); + + // Clean up tasks that have been in memory too long + if (tasksToRemove.length > 0) { + logger.info( + `Cleaning up tasks that have been in memory too long. Tasks: ${tasksToRemove.join( + ', ', + )}`, + ); + } + for (const queryId of tasksToRemove) { + this.trackedTasks.delete(queryId); + } + }); + } + + private async updateThreadResponseWhenTaskFinalized( + task: TrackedTask, + ): Promise { + const response = task?.result?.response?.[0]; + if (!response) { + return; + } + await this.threadResponseRepository.updateOne(task.threadResponseId, { + sql: response?.sql, + }); + } + + private async getAdjustmentResultFromDB({ + queryId, + taskId, + }: { + queryId?: string; + taskId?: number; + }): Promise { + let taskRecord: AskingTask | null = null; + if (queryId) { + taskRecord = await this.askingTaskRepository.findByQueryId(queryId); + } else if (taskId) { + taskRecord = await this.askingTaskRepository.findOneBy({ id: taskId }); + } + + if (!taskRecord) { + return null; + } + + return { + ...(taskRecord?.detail as AskFeedbackResult), + queryId: queryId || taskRecord?.queryId, + taskId: taskRecord?.id, + }; + } + + private async updateTaskInDatabase( + filter: { queryId?: string; taskId?: number }, + trackedTask: TrackedTask, + ): Promise { + const { queryId, taskId } = filter; + let taskRecord: AskingTask | null = null; + if (queryId) { + taskRecord = await this.askingTaskRepository.findByQueryId(queryId); + } else if (taskId) { + taskRecord = await this.askingTaskRepository.findOneBy({ id: taskId }); + } + + if (!taskRecord) { + throw new Error('Asking task not found'); + } + + // update the task + await this.askingTaskRepository.updateOne(taskRecord.id, { + detail: { + adjustment: true, + ...trackedTask.result, + }, + }); + } + + private isTaskFinalized(status: AskFeedbackStatus): boolean { + return [ + AskFeedbackStatus.FINISHED, + AskFeedbackStatus.FAILED, + AskFeedbackStatus.STOPPED, + ].includes(status); + } + + private isResultChanged( + previousResult: AskFeedbackResult, + newResult: AskFeedbackResult, + ): boolean { + // check status change + if (previousResult?.status !== newResult.status) { + return true; + } + + return false; + } +} diff --git a/wren-ui/src/apollo/server/backgrounds/index.ts b/wren-ui/src/apollo/server/backgrounds/index.ts index 3b3438be62..d6e6ef8131 100644 --- a/wren-ui/src/apollo/server/backgrounds/index.ts +++ b/wren-ui/src/apollo/server/backgrounds/index.ts @@ -1,2 +1,3 @@ export * from './recommend-question'; export * from './chart'; +export * from './adjustmentBackgroundTracker'; diff --git a/wren-ui/src/apollo/server/models/adaptor.ts b/wren-ui/src/apollo/server/models/adaptor.ts index b75efd649f..0af534fb53 100644 --- a/wren-ui/src/apollo/server/models/adaptor.ts +++ b/wren-ui/src/apollo/server/models/adaptor.ts @@ -291,3 +291,31 @@ export interface InstructionResult { status: InstructionStatus; error?: WrenAIError; } + +// ask feedback +export interface AskFeedbackInput { + tables: string[]; + sqlGenerationReasoning: string; + sql: string; + projectId: number; + configurations?: ProjectConfigurations; +} + +export enum AskFeedbackStatus { + UNDERSTANDING = 'UNDERSTANDING', + GENERATING = 'GENERATING', + CORRECTING = 'CORRECTING', + FINISHED = 'FINISHED', + FAILED = 'FAILED', + STOPPED = 'STOPPED', +} + +export interface AskFeedbackResult { + status: AskFeedbackStatus; + error?: WrenAIError; + response: Array<{ + type: AskCandidateType.LLM; + sql: string; + }>; + traceId?: string; +} diff --git a/wren-ui/src/apollo/server/repositories/askingTaskRepository.ts b/wren-ui/src/apollo/server/repositories/askingTaskRepository.ts index 889bbcf5d0..942f445ee1 100644 --- a/wren-ui/src/apollo/server/repositories/askingTaskRepository.ts +++ b/wren-ui/src/apollo/server/repositories/askingTaskRepository.ts @@ -7,13 +7,19 @@ import { mapValues, snakeCase, } from 'lodash'; -import { AskResult } from '../models/adaptor'; +import { AskFeedbackResult, AskResult } from '../models/adaptor'; + +export type AskingTaskDetail = + | AskResult + | (AskFeedbackResult & { + adjustment?: boolean; + }); export interface AskingTask { id: number; queryId: string; question?: string; - detail?: AskResult; + detail?: AskingTaskDetail; threadId?: number; threadResponseId?: number; createdAt: Date; diff --git a/wren-ui/src/apollo/server/repositories/threadResponseRepository.ts b/wren-ui/src/apollo/server/repositories/threadResponseRepository.ts index 87634989ac..b5219296f0 100644 --- a/wren-ui/src/apollo/server/repositories/threadResponseRepository.ts +++ b/wren-ui/src/apollo/server/repositories/threadResponseRepository.ts @@ -39,6 +39,26 @@ export interface ThreadResponseChartDetail { adjustment?: boolean; } +export enum ThreadResponseAdjustmentType { + REASONING = 'REASONING', +} + +export type ThreadResponseAdjustmentReasoningPayload = { + originalThreadResponseId: number; + retrievedTables?: string[]; + sqlGenerationReasoning?: string; +}; + +export type GenericThreadResponseAdjustment = { + type: T; + payload?: P; +}; + +export type ThreadResponseAdjustment = GenericThreadResponseAdjustment< + ThreadResponseAdjustmentType, + ThreadResponseAdjustmentReasoningPayload +>; + export interface ThreadResponse { id: number; // ID askingTaskId?: number; // Reference to asking_task.id @@ -49,6 +69,7 @@ export interface ThreadResponse { answerDetail?: ThreadResponseAnswerDetail; // AI generated text-based answer detail breakdownDetail?: ThreadResponseBreakdownDetail; // Thread response breakdown detail chartDetail?: ThreadResponseChartDetail; // Thread response chart detail + adjustment?: ThreadResponseAdjustment; // Thread response adjustment } export interface IThreadResponseRepository @@ -67,6 +88,7 @@ export class ThreadResponseRepository 'answerDetail', 'breakdownDetail', 'chartDetail', + 'adjustment', ]; constructor(knexPg: Knex) { @@ -102,11 +124,16 @@ export class ThreadResponseRepository res.chartDetail && typeof res.chartDetail === 'string' ? JSON.parse(res.chartDetail) : res.chartDetail; + const adjustment = + res.adjustment && typeof res.adjustment === 'string' + ? JSON.parse(res.adjustment) + : res.adjustment; return { ...res, answerDetail: answerDetail || null, breakdownDetail: breakdownDetail || null, chartDetail: chartDetail || null, + adjustment: adjustment || null, }; }) as ThreadResponse[]; } @@ -120,6 +147,7 @@ export class ThreadResponseRepository answerDetail: ThreadResponseAnswerDetail; breakdownDetail: ThreadResponseBreakdownDetail; chartDetail: ThreadResponseChartDetail; + adjustment: ThreadResponseAdjustment; }>, queryOptions?: IQueryOptions, ) { @@ -136,6 +164,7 @@ export class ThreadResponseRepository chartDetail: data.chartDetail ? JSON.stringify(data.chartDetail) : undefined, + adjustment: data.adjustment ? JSON.stringify(data.adjustment) : undefined, }; const executer = queryOptions?.tx ? queryOptions.tx : this.knex; const [result] = await executer(this.tableName) diff --git a/wren-ui/src/apollo/server/resolvers.ts b/wren-ui/src/apollo/server/resolvers.ts index 2e91bf22ac..fc01d77279 100644 --- a/wren-ui/src/apollo/server/resolvers.ts +++ b/wren-ui/src/apollo/server/resolvers.ts @@ -34,6 +34,9 @@ const resolvers = { suggestedQuestions: askingResolver.getSuggestedQuestions, instantRecommendedQuestions: askingResolver.getInstantRecommendedQuestions, + // Adjustment + adjustmentTask: askingResolver.getAdjustmentTask, + // Thread thread: askingResolver.getThread, threads: askingResolver.listThreads, @@ -97,6 +100,11 @@ const resolvers = { askingResolver.createInstantRecommendedQuestions, rerunAskingTask: askingResolver.rerunAskingTask, + // Adjustment + adjustThreadResponse: askingResolver.adjustThreadResponse, + cancelAdjustmentTask: askingResolver.cancelAdjustThreadResponseAnswer, + rerunAdjustmentTask: askingResolver.rerunAdjustThreadResponseAnswer, + // Thread createThread: askingResolver.createThread, updateThread: askingResolver.updateThread, diff --git a/wren-ui/src/apollo/server/resolvers/askingResolver.ts b/wren-ui/src/apollo/server/resolvers/askingResolver.ts index 31e499ec43..0412604b37 100644 --- a/wren-ui/src/apollo/server/resolvers/askingResolver.ts +++ b/wren-ui/src/apollo/server/resolvers/askingResolver.ts @@ -5,6 +5,7 @@ import { AskResultType, RecommendationQuestionStatus, ChartAdjustmentOption, + AskFeedbackStatus, } from '@server/models/adaptor'; import { Thread } from '../repositories/threadRepository'; import { @@ -39,6 +40,14 @@ export interface Task { id: string; } +export interface AdjustmentTask { + queryId: string; + status: AskFeedbackStatus; + error: WrenAIError | null; + sql: string; + traceId: string; +} + export interface AskingTask { type: AskResultType | null; status: AskResultStatus; @@ -108,6 +117,13 @@ export class AskingResolver { this.generateThreadResponseChart.bind(this); this.adjustThreadResponseChart = this.adjustThreadResponseChart.bind(this); this.transformAskingTask = this.transformAskingTask.bind(this); + + this.adjustThreadResponse = this.adjustThreadResponse.bind(this); + this.cancelAdjustThreadResponseAnswer = + this.cancelAdjustThreadResponseAnswer.bind(this); + this.rerunAdjustThreadResponseAnswer = + this.rerunAdjustThreadResponseAnswer.bind(this); + this.getAdjustmentTask = this.getAdjustmentTask.bind(this); } public async generateProjectRecommendationQuestions( @@ -304,6 +320,7 @@ export class AskingResolver { breakdownDetail: response.breakdownDetail, answerDetail: response.answerDetail, chartDetail: response.chartDetail, + adjustment: response.adjustment, }); return acc; @@ -448,6 +465,77 @@ export class AskingResolver { return task; } + public async adjustThreadResponse( + _root: any, + args: { + responseId: number; + data: { tables: string[]; sqlGenerationReasoning: string }; + }, + ctx: IContext, + ): Promise { + const { responseId, data } = args; + const askingService = ctx.askingService; + const project = await ctx.projectService.getCurrentProject(); + + return askingService.adjustThreadResponseAnswer( + responseId, + { + projectId: project.id, + tables: data.tables, + sqlGenerationReasoning: data.sqlGenerationReasoning, + }, + { + language: WrenAILanguage[project.language] || WrenAILanguage.EN, + }, + ); + } + + public async cancelAdjustThreadResponseAnswer( + _root: any, + args: { responseId: number }, + ctx: IContext, + ): Promise { + const { responseId } = args; + const askingService = ctx.askingService; + await askingService.cancelAdjustThreadResponseAnswer(responseId); + return true; + } + + public async rerunAdjustThreadResponseAnswer( + _root: any, + args: { responseId: number }, + ctx: IContext, + ): Promise { + const { responseId } = args; + const askingService = ctx.askingService; + const project = await ctx.projectService.getCurrentProject(); + await askingService.rerunAdjustThreadResponseAnswer( + responseId, + project.id, + { + language: WrenAILanguage[project.language] || WrenAILanguage.EN, + }, + ); + return true; + } + + public async getAdjustmentTask( + _root: any, + args: { taskId: string }, + ctx: IContext, + ): Promise { + const { taskId } = args; + const askingService = ctx.askingService; + const adjustmentTask = await askingService.getAdjustmentTask(taskId); + return { + queryId: adjustmentTask?.queryId, + status: adjustmentTask?.status, + error: adjustmentTask?.error, + sql: adjustmentTask?.response?.[0]?.sql, + traceId: adjustmentTask?.traceId, + }; + } + public async generateThreadResponseBreakdown( _root: any, args: { responseId: number }, @@ -604,6 +692,9 @@ export class AskingResolver { return parent.sql ? format(parent.sql) : null; }, askingTask: async (parent: ThreadResponse, _args: any, ctx: IContext) => { + if (parent.adjustment) { + return null; + } const askingService = ctx.askingService; const askingTask = await askingService.getAskingTaskById( parent.askingTaskId, @@ -611,6 +702,26 @@ export class AskingResolver { if (!askingTask) return null; return this.transformAskingTask(askingTask, ctx); }, + adjustmentTask: async ( + parent: ThreadResponse, + _args: any, + ctx: IContext, + ): Promise => { + if (!parent.adjustment) { + return null; + } + const askingService = ctx.askingService; + const adjustmentTask = await askingService.getAdjustmentTaskById( + parent.askingTaskId, + ); + return { + queryId: adjustmentTask?.queryId, + status: adjustmentTask?.status, + error: adjustmentTask?.error, + sql: adjustmentTask?.response?.[0]?.sql, + traceId: adjustmentTask?.traceId, + }; + }, }); public getDetailStepNestedResolver = () => ({ diff --git a/wren-ui/src/apollo/server/schema.ts b/wren-ui/src/apollo/server/schema.ts index 5e9012723d..3a45268bae 100644 --- a/wren-ui/src/apollo/server/schema.ts +++ b/wren-ui/src/apollo/server/schema.ts @@ -656,6 +656,11 @@ export const typeDefs = gql` theta: String } + input AdjustThreadResponseInput { + tables: [String!]! + sqlGenerationReasoning: String! + } + input PreviewDataInput { responseId: Int! # Optional, only used for preview data of a single step @@ -707,6 +712,23 @@ export const typeDefs = gql` adjustment: Boolean } + enum ThreadResponseAdjustmentType { + REASONING + } + + type ThreadResponseAdjustment { + type: ThreadResponseAdjustmentType! + payload: JSON + } + + type AdjustmentTask { + queryId: String + status: AskingTaskStatus! + error: Error + sql: String + traceId: String + } + type ThreadResponse { id: Int! threadId: Int! @@ -717,6 +739,8 @@ export const typeDefs = gql` answerDetail: ThreadResponseAnswerDetail chartDetail: ThreadResponseChartDetail askingTask: AskingTask + adjustment: ThreadResponseAdjustment + adjustmentTask: AdjustmentTask } # Thread only consists of basic information of a thread @@ -955,6 +979,9 @@ export const typeDefs = gql` threadResponse(responseId: Int!): ThreadResponse! nativeSql(responseId: Int!): String! + # Adjustment + adjustmentTask(taskId: String!): AdjustmentTask + # Settings settings: Settings! @@ -1066,6 +1093,14 @@ export const typeDefs = gql` data: AdjustThreadResponseChartInput! ): ThreadResponse! + # Adjustment + adjustThreadResponse( + responseId: Int! + data: AdjustThreadResponseInput! + ): ThreadResponse! + cancelAdjustmentTask(queryId: String!): Boolean! + rerunAdjustmentTask(responseId: Int!): Task! + # Settings resetCurrentProject: Boolean! updateCurrentProject(data: UpdateCurrentProjectInput!): Boolean! diff --git a/wren-ui/src/apollo/server/services/askingService.ts b/wren-ui/src/apollo/server/services/askingService.ts index 78eed90901..524ee29e1d 100644 --- a/wren-ui/src/apollo/server/services/askingService.ts +++ b/wren-ui/src/apollo/server/services/askingService.ts @@ -9,6 +9,7 @@ import { ChartStatus, ChartAdjustmentOption, WrenAILanguage, + ProjectConfigurations, } from '@server/models/adaptor'; import { IDeployService } from './deployService'; import { IProjectService } from './projectService'; @@ -25,13 +26,19 @@ import { TelemetryEvent, WrenService, } from '../telemetry/telemetry'; -import { IViewRepository, Project } from '../repositories'; +import { + IAskingTaskRepository, + IViewRepository, + Project, +} from '../repositories'; import { IQueryService, PreviewDataResponse } from './queryService'; import { IMDLService } from './mdlService'; import { ThreadRecommendQuestionBackgroundTracker, ChartBackgroundTracker, ChartAdjustmentBackgroundTracker, + AdjustmentBackgroundTaskTracker, + TrackedAdjustmentResult, } from '../backgrounds'; import { getConfig } from '@server/config'; import { TextBasedAnswerBackgroundTracker } from '../backgrounds/textBasedAnswerBackgroundTracker'; @@ -94,6 +101,13 @@ export enum ThreadResponseAnswerStatus { INTERRUPTED = 'INTERRUPTED', } +// adjustment input +export interface AdjustmentInput { + tables: string[]; + sqlGenerationReasoning: string; + projectId: number; +} + export interface IAskingService { /** * Asking task. @@ -155,6 +169,19 @@ export interface IAskingService { input: ChartAdjustmentOption, configurations: { language: string }, ): Promise; + adjustThreadResponseAnswer( + threadResponseId: number, + input: AdjustmentInput, + configurations: { language: string }, + ): Promise; + cancelAdjustThreadResponseAnswer(threadResponseId: number): Promise; + rerunAdjustThreadResponseAnswer( + threadResponseId: number, + projectId: number, + configurations: ProjectConfigurations, + ): Promise<{ queryId: string }>; + getAdjustmentTask(taskId: string): Promise; + getAdjustmentTaskById(id: number): Promise; changeThreadResponseAnswerDetailStatus( responseId: number, status: ThreadResponseAnswerStatus, @@ -379,6 +406,8 @@ export class AskingService implements IAskingService { private telemetry: PostHogTelemetry; private mdlService: IMDLService; private askingTaskTracker: IAskingTaskTracker; + private askingTaskRepository: IAskingTaskRepository; + private adjustmentBackgroundTracker: AdjustmentBackgroundTaskTracker; constructor({ telemetry, @@ -388,6 +417,7 @@ export class AskingService implements IAskingService { viewRepository, threadRepository, threadResponseRepository, + askingTaskRepository, queryService, mdlService, askingTaskTracker, @@ -399,6 +429,7 @@ export class AskingService implements IAskingService { viewRepository: IViewRepository; threadRepository: IThreadRepository; threadResponseRepository: IThreadResponseRepository; + askingTaskRepository: IAskingTaskRepository; queryService: IQueryService; mdlService: IMDLService; askingTaskTracker: IAskingTaskTracker; @@ -441,7 +472,13 @@ export class AskingService implements IAskingService { wrenAIAdaptor, threadRepository, }); + this.adjustmentBackgroundTracker = new AdjustmentBackgroundTaskTracker({ + wrenAIAdaptor, + askingTaskRepository, + threadResponseRepository, + }); + this.askingTaskRepository = askingTaskRepository; this.mdlService = mdlService; this.askingTaskTracker = askingTaskTracker; } @@ -1013,6 +1050,86 @@ export class AskingService implements IAskingService { return lastDeploy.hash; } + public async adjustThreadResponseAnswer( + threadResponseId: number, + input: AdjustmentInput, + configurations: { language: string }, + ): Promise { + const originalThreadResponse = + await this.threadResponseRepository.findOneBy({ + id: threadResponseId, + }); + if (!originalThreadResponse) { + throw new Error(`Thread response ${threadResponseId} not found`); + } + + const { createdThreadResponse } = + await this.adjustmentBackgroundTracker.createAdjustmentTask({ + threadId: originalThreadResponse.threadId, + tables: input.tables, + sqlGenerationReasoning: input.sqlGenerationReasoning, + sql: originalThreadResponse.sql, + projectId: input.projectId, + configurations, + question: originalThreadResponse.question, + originalThreadResponseId: originalThreadResponse.id, + }); + return createdThreadResponse; + } + + public async cancelAdjustThreadResponseAnswer( + threadResponseId: number, + ): Promise { + const askingTask = await this.askingTaskRepository.findOneBy({ + threadResponseId, + }); + if (!askingTask) { + throw new Error(`Asking task ${threadResponseId} not found`); + } + + // call cancelAskFeedback on AI service + await this.adjustmentBackgroundTracker.cancelAdjustmentTask( + askingTask.queryId, + ); + } + + public async rerunAdjustThreadResponseAnswer( + threadResponseId: number, + projectId: number, + configurations: ProjectConfigurations, + ): Promise<{ queryId: string }> { + const threadResponse = await this.threadResponseRepository.findOneBy({ + id: threadResponseId, + }); + if (!threadResponse) { + throw new Error(`Thread response ${threadResponseId} not found`); + } + const adjustment = threadResponse.adjustment; + if (!adjustment) { + throw new Error(`Thread response ${threadResponseId} has no adjustment`); + } + const { queryId } = + await this.adjustmentBackgroundTracker.rerunAdjustmentTask({ + threadId: threadResponse.threadId, + threadResponseId, + projectId, + configurations, + }); + return { queryId }; + } + + public async getAdjustmentTask( + taskId: string, + ): Promise { + return this.adjustmentBackgroundTracker.getAdjustmentResult(taskId); + } + + public async getAdjustmentTaskById( + id: number, + ): Promise { + return this.adjustmentBackgroundTracker.getAdjustmentResultById(id); + } + /** * Get the thread response of a thread for asking * @param threadId diff --git a/wren-ui/src/apollo/server/services/askingTaskTracker.ts b/wren-ui/src/apollo/server/services/askingTaskTracker.ts index 2ed0274abe..a2f18a61f0 100644 --- a/wren-ui/src/apollo/server/services/askingTaskTracker.ts +++ b/wren-ui/src/apollo/server/services/askingTaskTracker.ts @@ -404,7 +404,7 @@ export class AskingTaskTracker implements IAskingTaskTracker { } return { - ...taskRecord?.detail, + ...(taskRecord?.detail as AskResult), queryId: queryId || taskRecord?.queryId, question: taskRecord?.question, taskId: taskRecord?.id, diff --git a/wren-ui/src/apollo/server/telemetry/telemetry.ts b/wren-ui/src/apollo/server/telemetry/telemetry.ts index 99c469d54a..1ac80c2122 100644 --- a/wren-ui/src/apollo/server/telemetry/telemetry.ts +++ b/wren-ui/src/apollo/server/telemetry/telemetry.ts @@ -56,6 +56,9 @@ export enum TelemetryEvent { HOME_GENERATE_PROJECT_RECOMMENDATION_QUESTIONS = 'home_generate_project_recommendation_questions', HOME_GENERATE_THREAD_RECOMMENDATION_QUESTIONS = 'home_generate_thread_recommendation_questions', + // adjustment + HOME_ADJUST_THREAD_RESPONSE = 'home_adjust_thread_response', + // event after ask HOME_CREATE_VIEW = 'home_create_view', HOME_PREVIEW_ANSWER = 'home_preview_answer', diff --git a/wren-ui/src/common.ts b/wren-ui/src/common.ts index aee4518976..8337b2b6c2 100644 --- a/wren-ui/src/common.ts +++ b/wren-ui/src/common.ts @@ -128,6 +128,7 @@ export const initComponents = () => { queryService, mdlService, askingTaskTracker, + askingTaskRepository, }); const dashboardService = new DashboardService({ projectService, From d963ace426a456e40c25285b660d4f7b34beb3bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?william=20chang=28=E5=BC=B5=E4=BB=B2=E5=A8=81=29?= Date: Tue, 1 Apr 2025 03:03:06 +0800 Subject: [PATCH 17/22] cancel & rerun --- .../apollo/server/resolvers/askingResolver.ts | 6 +++--- wren-ui/src/apollo/server/schema.ts | 4 ++-- .../src/apollo/server/services/askingService.ts | 17 +++-------------- 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/wren-ui/src/apollo/server/resolvers/askingResolver.ts b/wren-ui/src/apollo/server/resolvers/askingResolver.ts index 0412604b37..5c4fa6c327 100644 --- a/wren-ui/src/apollo/server/resolvers/askingResolver.ts +++ b/wren-ui/src/apollo/server/resolvers/askingResolver.ts @@ -492,12 +492,12 @@ export class AskingResolver { public async cancelAdjustThreadResponseAnswer( _root: any, - args: { responseId: number }, + args: { taskId: string }, ctx: IContext, ): Promise { - const { responseId } = args; + const { taskId } = args; const askingService = ctx.askingService; - await askingService.cancelAdjustThreadResponseAnswer(responseId); + await askingService.cancelAdjustThreadResponseAnswer(taskId); return true; } diff --git a/wren-ui/src/apollo/server/schema.ts b/wren-ui/src/apollo/server/schema.ts index 3a45268bae..963ef12108 100644 --- a/wren-ui/src/apollo/server/schema.ts +++ b/wren-ui/src/apollo/server/schema.ts @@ -1098,8 +1098,8 @@ export const typeDefs = gql` responseId: Int! data: AdjustThreadResponseInput! ): ThreadResponse! - cancelAdjustmentTask(queryId: String!): Boolean! - rerunAdjustmentTask(responseId: Int!): Task! + cancelAdjustmentTask(taskId: String!): Boolean! + rerunAdjustmentTask(responseId: Int!): Boolean! # Settings resetCurrentProject: Boolean! diff --git a/wren-ui/src/apollo/server/services/askingService.ts b/wren-ui/src/apollo/server/services/askingService.ts index 524ee29e1d..e72c19efa6 100644 --- a/wren-ui/src/apollo/server/services/askingService.ts +++ b/wren-ui/src/apollo/server/services/askingService.ts @@ -174,7 +174,7 @@ export interface IAskingService { input: AdjustmentInput, configurations: { language: string }, ): Promise; - cancelAdjustThreadResponseAnswer(threadResponseId: number): Promise; + cancelAdjustThreadResponseAnswer(taskId: string): Promise; rerunAdjustThreadResponseAnswer( threadResponseId: number, projectId: number, @@ -1077,20 +1077,9 @@ export class AskingService implements IAskingService { return createdThreadResponse; } - public async cancelAdjustThreadResponseAnswer( - threadResponseId: number, - ): Promise { - const askingTask = await this.askingTaskRepository.findOneBy({ - threadResponseId, - }); - if (!askingTask) { - throw new Error(`Asking task ${threadResponseId} not found`); - } - + public async cancelAdjustThreadResponseAnswer(taskId: string): Promise { // call cancelAskFeedback on AI service - await this.adjustmentBackgroundTracker.cancelAdjustmentTask( - askingTask.queryId, - ); + await this.adjustmentBackgroundTracker.cancelAdjustmentTask(taskId); } public async rerunAdjustThreadResponseAnswer( From 40972bb6c9b504d94fdec5a2bee3947c287cdf22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?william=20chang=28=E5=BC=B5=E4=BB=B2=E5=A8=81=29?= Date: Tue, 1 Apr 2025 03:21:53 +0800 Subject: [PATCH 18/22] adjust thread response with SQL --- .../repositories/threadResponseRepository.ts | 19 ++++++----- .../apollo/server/resolvers/askingResolver.ts | 13 ++++++- wren-ui/src/apollo/server/schema.ts | 8 +++-- .../apollo/server/services/askingService.ts | 34 +++++++++++++++++++ 4 files changed, 62 insertions(+), 12 deletions(-) diff --git a/wren-ui/src/apollo/server/repositories/threadResponseRepository.ts b/wren-ui/src/apollo/server/repositories/threadResponseRepository.ts index b5219296f0..f51d1eda91 100644 --- a/wren-ui/src/apollo/server/repositories/threadResponseRepository.ts +++ b/wren-ui/src/apollo/server/repositories/threadResponseRepository.ts @@ -41,23 +41,26 @@ export interface ThreadResponseChartDetail { export enum ThreadResponseAdjustmentType { REASONING = 'REASONING', + APPLY_SQL = 'APPLY_SQL', } export type ThreadResponseAdjustmentReasoningPayload = { - originalThreadResponseId: number; + originalThreadResponseId?: number; retrievedTables?: string[]; sqlGenerationReasoning?: string; }; -export type GenericThreadResponseAdjustment = { - type: T; - payload?: P; +export type ThreadResponseAdjustmentApplySqlPayload = { + originalThreadResponseId?: number; + sql?: string; }; -export type ThreadResponseAdjustment = GenericThreadResponseAdjustment< - ThreadResponseAdjustmentType, - ThreadResponseAdjustmentReasoningPayload ->; +export interface ThreadResponseAdjustment { + type: ThreadResponseAdjustmentType; + // todo: I think we could use a better way to do this instead of using a union type + payload: ThreadResponseAdjustmentReasoningPayload & + ThreadResponseAdjustmentApplySqlPayload; +} export interface ThreadResponse { id: number; // ID diff --git a/wren-ui/src/apollo/server/resolvers/askingResolver.ts b/wren-ui/src/apollo/server/resolvers/askingResolver.ts index 5c4fa6c327..10e8862fcc 100644 --- a/wren-ui/src/apollo/server/resolvers/askingResolver.ts +++ b/wren-ui/src/apollo/server/resolvers/askingResolver.ts @@ -469,7 +469,11 @@ export class AskingResolver { _root: any, args: { responseId: number; - data: { tables: string[]; sqlGenerationReasoning: string }; + data: { + tables?: string[]; + sqlGenerationReasoning?: string; + sql?: string; + }; }, ctx: IContext, ): Promise { @@ -477,6 +481,12 @@ export class AskingResolver { const askingService = ctx.askingService; const project = await ctx.projectService.getCurrentProject(); + if (data.sql) { + return askingService.adjustThreadResponseWithSQL(responseId, { + sql: data.sql, + }); + } + return askingService.adjustThreadResponseAnswer( responseId, { @@ -714,6 +724,7 @@ export class AskingResolver { const adjustmentTask = await askingService.getAdjustmentTaskById( parent.askingTaskId, ); + if (!adjustmentTask) return null; return { queryId: adjustmentTask?.queryId, status: adjustmentTask?.status, diff --git a/wren-ui/src/apollo/server/schema.ts b/wren-ui/src/apollo/server/schema.ts index 963ef12108..35da5187f8 100644 --- a/wren-ui/src/apollo/server/schema.ts +++ b/wren-ui/src/apollo/server/schema.ts @@ -657,8 +657,9 @@ export const typeDefs = gql` } input AdjustThreadResponseInput { - tables: [String!]! - sqlGenerationReasoning: String! + tables: [String!] + sqlGenerationReasoning: String + sql: String } input PreviewDataInput { @@ -714,6 +715,7 @@ export const typeDefs = gql` enum ThreadResponseAdjustmentType { REASONING + APPLY_SQL } type ThreadResponseAdjustment { @@ -723,7 +725,7 @@ export const typeDefs = gql` type AdjustmentTask { queryId: String - status: AskingTaskStatus! + status: AskingTaskStatus error: Error sql: String traceId: String diff --git a/wren-ui/src/apollo/server/services/askingService.ts b/wren-ui/src/apollo/server/services/askingService.ts index e72c19efa6..95fce91832 100644 --- a/wren-ui/src/apollo/server/services/askingService.ts +++ b/wren-ui/src/apollo/server/services/askingService.ts @@ -17,6 +17,7 @@ import { IThreadRepository, Thread } from '../repositories/threadRepository'; import { IThreadResponseRepository, ThreadResponse, + ThreadResponseAdjustmentType, } from '../repositories/threadResponseRepository'; import { getLogger } from '@server/utils'; import { isEmpty, isNil } from 'lodash'; @@ -108,6 +109,10 @@ export interface AdjustmentInput { projectId: number; } +export interface AdjustmentApplySqlInput { + sql: string; +} + export interface IAskingService { /** * Asking task. @@ -169,6 +174,10 @@ export interface IAskingService { input: ChartAdjustmentOption, configurations: { language: string }, ): Promise; + adjustThreadResponseWithSQL( + threadResponseId: number, + input: AdjustmentApplySqlInput, + ): Promise; adjustThreadResponseAnswer( threadResponseId: number, input: AdjustmentInput, @@ -1050,6 +1059,31 @@ export class AskingService implements IAskingService { return lastDeploy.hash; } + public async adjustThreadResponseWithSQL( + threadResponseId: number, + input: AdjustmentApplySqlInput, + ): Promise { + const response = await this.threadResponseRepository.findOneBy({ + id: threadResponseId, + }); + if (!response) { + throw new Error(`Thread response ${threadResponseId} not found`); + } + + return await this.threadResponseRepository.createOne({ + sql: input.sql, + threadId: response.threadId, + question: response.question, + adjustment: { + type: ThreadResponseAdjustmentType.APPLY_SQL, + payload: { + originalThreadResponseId: response.id, + sql: input.sql, + }, + }, + }); + } + public async adjustThreadResponseAnswer( threadResponseId: number, input: AdjustmentInput, From b7556bc1ee03be7bb7295e1a4ecf285cf77d3da4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?william=20chang=28=E5=BC=B5=E4=BB=B2=E5=A8=81=29?= Date: Tue, 1 Apr 2025 11:57:34 +0800 Subject: [PATCH 19/22] change project id type to string --- wren-ui/src/apollo/server/adaptors/wrenAIAdaptor.ts | 1 + wren-ui/src/apollo/server/models/model.ts | 2 +- wren-ui/src/apollo/server/resolvers/modelResolver.ts | 2 +- wren-ui/src/apollo/server/schema.ts | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/wren-ui/src/apollo/server/adaptors/wrenAIAdaptor.ts b/wren-ui/src/apollo/server/adaptors/wrenAIAdaptor.ts index a5f6b0731b..21c747bed1 100644 --- a/wren-ui/src/apollo/server/adaptors/wrenAIAdaptor.ts +++ b/wren-ui/src/apollo/server/adaptors/wrenAIAdaptor.ts @@ -665,6 +665,7 @@ export class WrenAIAdaptor implements IWrenAIAdaptor { tables: input.tables, sql_generation_reasoning: input.sqlGenerationReasoning, sql: input.sql, + project_id: input.projectId.toString(), configurations: input.configurations, }; diff --git a/wren-ui/src/apollo/server/models/model.ts b/wren-ui/src/apollo/server/models/model.ts index afb1029f79..fd763d3de6 100644 --- a/wren-ui/src/apollo/server/models/model.ts +++ b/wren-ui/src/apollo/server/models/model.ts @@ -91,7 +91,7 @@ export interface CheckCalculatedFieldCanQueryData { export interface PreviewSQLData { sql: string; - projectId?: number; + projectId?: string; limit?: number; dryRun?: boolean; } diff --git a/wren-ui/src/apollo/server/resolvers/modelResolver.ts b/wren-ui/src/apollo/server/resolvers/modelResolver.ts index 5711d70765..c80b511690 100644 --- a/wren-ui/src/apollo/server/resolvers/modelResolver.ts +++ b/wren-ui/src/apollo/server/resolvers/modelResolver.ts @@ -932,7 +932,7 @@ export class ModelResolver { ) { const { sql, projectId, limit, dryRun } = args.data; const project = projectId - ? await ctx.projectService.getProjectById(projectId) + ? await ctx.projectService.getProjectById(parseInt(projectId)) : await ctx.projectService.getCurrentProject(); const { manifest } = await ctx.deployService.getLastDeployment(project.id); return await ctx.queryService.preview(sql, { diff --git a/wren-ui/src/apollo/server/schema.ts b/wren-ui/src/apollo/server/schema.ts index 35da5187f8..2a7835d4f2 100644 --- a/wren-ui/src/apollo/server/schema.ts +++ b/wren-ui/src/apollo/server/schema.ts @@ -788,7 +788,7 @@ export const typeDefs = gql` input PreviewSQLDataInput { sql: String! - projectId: Int + projectId: String limit: Int dryRun: Boolean } From 51a8776d477f7fe65480adb80de4f1fa68cd8436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?william=20chang=28=E5=BC=B5=E4=BB=B2=E5=A8=81=29?= Date: Wed, 2 Apr 2025 12:57:02 +0800 Subject: [PATCH 20/22] fix that preview data failure will not show error message from engine --- .../apollo/server/adaptors/wrenEngineAdaptor.ts | 15 ++++++++++++--- wren-ui/src/apollo/server/utils/error.ts | 3 +++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/wren-ui/src/apollo/server/adaptors/wrenEngineAdaptor.ts b/wren-ui/src/apollo/server/adaptors/wrenEngineAdaptor.ts index c946c9b7a0..f849cffb88 100644 --- a/wren-ui/src/apollo/server/adaptors/wrenEngineAdaptor.ts +++ b/wren-ui/src/apollo/server/adaptors/wrenEngineAdaptor.ts @@ -200,7 +200,10 @@ export class WrenEngineAdaptor implements IWrenEngineAdaptor { return res.data as EngineQueryResponse; } catch (err: any) { logger.debug(`Got error when querying duckdb: ${err.message}`); - throw err; + throw Errors.create(Errors.GeneralErrorCodes.WREN_ENGINE_ERROR, { + customMessage: err.response?.data?.message || err.message, + originalError: err, + }); } } @@ -219,7 +222,10 @@ export class WrenEngineAdaptor implements IWrenEngineAdaptor { await axios.patch(url.href, configPayload, { headers }); } catch (err: any) { logger.debug(`Got error when patching config: ${err.message}`); - throw err; + throw Errors.create(Errors.GeneralErrorCodes.WREN_ENGINE_ERROR, { + customMessage: err.response?.data?.message || err.message, + originalError: err, + }); } } @@ -248,7 +254,10 @@ export class WrenEngineAdaptor implements IWrenEngineAdaptor { return res.data; } catch (err: any) { logger.debug(`Got error when previewing data: ${err.message}`); - throw err; + throw Errors.create(Errors.GeneralErrorCodes.WREN_ENGINE_ERROR, { + customMessage: err.response?.data?.message || err.message, + originalError: err, + }); } } diff --git a/wren-ui/src/apollo/server/utils/error.ts b/wren-ui/src/apollo/server/utils/error.ts index 58fc196401..87b8069033 100644 --- a/wren-ui/src/apollo/server/utils/error.ts +++ b/wren-ui/src/apollo/server/utils/error.ts @@ -41,6 +41,9 @@ export enum GeneralErrorCodes { GENERATE_QUESTIONS_ERROR = 'GENERATE_QUESTIONS_ERROR', INVALID_SQL_ERROR = 'INVALID_SQL_ERROR', + // wren engine error + WREN_ENGINE_ERROR = 'WREN_ENGINE_ERROR', + // asking task error // when rerun from cancelled, the task is identified as general or misleading query IDENTIED_AS_GENERAL = 'IDENTIED_AS_GENERAL', From 593d473ea0a0f4f21177a4f23f281e5ccdaf04e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?william=20chang=28=E5=BC=B5=E4=BB=B2=E5=A8=81=29?= Date: Wed, 2 Apr 2025 12:57:49 +0800 Subject: [PATCH 21/22] fix comments & add telemetry --- .../adjustmentBackgroundTracker.ts | 51 +++++++++++++++++++ .../apollo/server/resolvers/askingResolver.ts | 17 +++++-- .../apollo/server/services/askingService.ts | 18 +++---- .../src/apollo/server/telemetry/telemetry.ts | 3 ++ 4 files changed, 76 insertions(+), 13 deletions(-) diff --git a/wren-ui/src/apollo/server/backgrounds/adjustmentBackgroundTracker.ts b/wren-ui/src/apollo/server/backgrounds/adjustmentBackgroundTracker.ts index a5050fd2d2..12fc45efc2 100644 --- a/wren-ui/src/apollo/server/backgrounds/adjustmentBackgroundTracker.ts +++ b/wren-ui/src/apollo/server/backgrounds/adjustmentBackgroundTracker.ts @@ -13,6 +13,8 @@ import { ThreadResponseAdjustmentType, } from '@server/repositories'; import { IWrenAIAdaptor } from '../adaptors'; +import { TelemetryEvent, WrenService } from '../telemetry/telemetry'; +import { PostHogTelemetry } from '../telemetry/telemetry'; const logger = getLogger('AdjustmentTaskTracker'); logger.level = 'debug'; @@ -26,6 +28,12 @@ interface TrackedTask { threadResponseId: number; question: string; originalThreadResponseId: number; + rerun?: boolean; + adjustmentPayload?: { + originalThreadResponseId: number; + retrievedTables: string[]; + sqlGenerationReasoning: string; + }; } export type TrackedAdjustmentResult = AskFeedbackResult & { @@ -70,20 +78,24 @@ export class AdjustmentBackgroundTaskTracker private pollingIntervalId: NodeJS.Timeout; private runningJobs = new Set(); private threadResponseRepository: IThreadResponseRepository; + private telemetry: PostHogTelemetry; constructor({ + telemetry, wrenAIAdaptor, askingTaskRepository, threadResponseRepository, pollingInterval = 1000, // 1 second memoryRetentionTime = 5 * 60 * 1000, // 5 minutes }: { + telemetry: PostHogTelemetry; wrenAIAdaptor: IWrenAIAdaptor; askingTaskRepository: IAskingTaskRepository; threadResponseRepository: IThreadResponseRepository; pollingInterval?: number; memoryRetentionTime?: number; }) { + this.telemetry = telemetry; this.wrenAIAdaptor = wrenAIAdaptor; this.askingTaskRepository = askingTaskRepository; this.threadResponseRepository = threadResponseRepository; @@ -144,6 +156,11 @@ export class AdjustmentBackgroundTaskTracker originalThreadResponseId: input.originalThreadResponseId, threadResponseId: createdThreadResponse.id, question: input.question, + adjustmentPayload: { + originalThreadResponseId: input.originalThreadResponseId, + retrievedTables: input.tables, + sqlGenerationReasoning: input.sqlGenerationReasoning, + }, } as TrackedTask; this.trackedTasks.set(queryId, task); this.trackedTasksById.set(createdThreadResponse.id, task); @@ -218,6 +235,12 @@ export class AdjustmentBackgroundTaskTracker originalThreadResponseId: originalThreadResponse.id, threadResponseId: currentThreadResponse.id, question: originalThreadResponse.question, + rerun: true, + adjustmentPayload: { + originalThreadResponseId: originalThreadResponse.id, + retrievedTables: adjustment.payload?.retrievedTables, + sqlGenerationReasoning: adjustment.payload?.sqlGenerationReasoning, + }, } as TrackedTask; this.trackedTasks.set(queryId, task); this.trackedTasksById.set(currentThreadResponse.id, task); @@ -257,6 +280,12 @@ export class AdjustmentBackgroundTaskTracker public async cancelAdjustmentTask(queryId: string): Promise { await this.wrenAIAdaptor.cancelAskFeedback(queryId); + + // telemetry + const eventName = TelemetryEvent.HOME_ADJUST_THREAD_RESPONSE_CANCEL; + this.telemetry.sendEvent(eventName, { + queryId, + }); } public stopPolling(): void { @@ -329,6 +358,28 @@ export class AdjustmentBackgroundTaskTracker await this.updateThreadResponseWhenTaskFinalized(task); } + // telemetry + const eventName = task.rerun + ? TelemetryEvent.HOME_ADJUST_THREAD_RESPONSE_RERUN + : TelemetryEvent.HOME_ADJUST_THREAD_RESPONSE; + const eventProperties = { + taskId: task.taskId, + queryId: task.queryId, + status: result.status, + error: result.error, + adjustmentPayload: task.adjustmentPayload, + }; + if (result.status === AskFeedbackStatus.FINISHED) { + this.telemetry.sendEvent(eventName, eventProperties); + } else { + this.telemetry.sendEvent( + eventName, + eventProperties, + WrenService.AI, + false, + ); + } + logger.info( `Task ${queryId} is finalized with status: ${result.status}`, ); diff --git a/wren-ui/src/apollo/server/resolvers/askingResolver.ts b/wren-ui/src/apollo/server/resolvers/askingResolver.ts index 10e8862fcc..e67fb451b3 100644 --- a/wren-ui/src/apollo/server/resolvers/askingResolver.ts +++ b/wren-ui/src/apollo/server/resolvers/askingResolver.ts @@ -482,9 +482,20 @@ export class AskingResolver { const project = await ctx.projectService.getCurrentProject(); if (data.sql) { - return askingService.adjustThreadResponseWithSQL(responseId, { - sql: data.sql, - }); + const response = await askingService.adjustThreadResponseWithSQL( + responseId, + { + sql: data.sql, + }, + ); + ctx.telemetry.sendEvent( + TelemetryEvent.HOME_ADJUST_THREAD_RESPONSE_WITH_SQL, + { + sql: data.sql, + responseId, + }, + ); + return response; } return askingService.adjustThreadResponseAnswer( diff --git a/wren-ui/src/apollo/server/services/askingService.ts b/wren-ui/src/apollo/server/services/askingService.ts index 95fce91832..184f4a58ee 100644 --- a/wren-ui/src/apollo/server/services/askingService.ts +++ b/wren-ui/src/apollo/server/services/askingService.ts @@ -103,13 +103,13 @@ export enum ThreadResponseAnswerStatus { } // adjustment input -export interface AdjustmentInput { +export interface AdjustmentReasoningInput { tables: string[]; sqlGenerationReasoning: string; projectId: number; } -export interface AdjustmentApplySqlInput { +export interface AdjustmentSqlInput { sql: string; } @@ -176,11 +176,11 @@ export interface IAskingService { ): Promise; adjustThreadResponseWithSQL( threadResponseId: number, - input: AdjustmentApplySqlInput, + input: AdjustmentSqlInput, ): Promise; adjustThreadResponseAnswer( threadResponseId: number, - input: AdjustmentInput, + input: AdjustmentReasoningInput, configurations: { language: string }, ): Promise; cancelAdjustThreadResponseAnswer(taskId: string): Promise; @@ -482,6 +482,7 @@ export class AskingService implements IAskingService { threadRepository, }); this.adjustmentBackgroundTracker = new AdjustmentBackgroundTaskTracker({ + telemetry, wrenAIAdaptor, askingTaskRepository, threadResponseRepository, @@ -1061,7 +1062,7 @@ export class AskingService implements IAskingService { public async adjustThreadResponseWithSQL( threadResponseId: number, - input: AdjustmentApplySqlInput, + input: AdjustmentSqlInput, ): Promise { const response = await this.threadResponseRepository.findOneBy({ id: threadResponseId, @@ -1086,7 +1087,7 @@ export class AskingService implements IAskingService { public async adjustThreadResponseAnswer( threadResponseId: number, - input: AdjustmentInput, + input: AdjustmentReasoningInput, configurations: { language: string }, ): Promise { const originalThreadResponse = @@ -1127,10 +1128,7 @@ export class AskingService implements IAskingService { if (!threadResponse) { throw new Error(`Thread response ${threadResponseId} not found`); } - const adjustment = threadResponse.adjustment; - if (!adjustment) { - throw new Error(`Thread response ${threadResponseId} has no adjustment`); - } + const { queryId } = await this.adjustmentBackgroundTracker.rerunAdjustmentTask({ threadId: threadResponse.threadId, diff --git a/wren-ui/src/apollo/server/telemetry/telemetry.ts b/wren-ui/src/apollo/server/telemetry/telemetry.ts index 1ac80c2122..930982013b 100644 --- a/wren-ui/src/apollo/server/telemetry/telemetry.ts +++ b/wren-ui/src/apollo/server/telemetry/telemetry.ts @@ -58,6 +58,9 @@ export enum TelemetryEvent { // adjustment HOME_ADJUST_THREAD_RESPONSE = 'home_adjust_thread_response', + HOME_ADJUST_THREAD_RESPONSE_CANCEL = 'home_adjust_thread_response_cancel', + HOME_ADJUST_THREAD_RESPONSE_RERUN = 'home_adjust_thread_response_rerun', + HOME_ADJUST_THREAD_RESPONSE_WITH_SQL = 'home_adjust_thread_response_with_sql', // event after ask HOME_CREATE_VIEW = 'home_create_view', From 794f1aef015176617ee3d06175fe94b657f84cbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?william=20chang=28=E5=BC=B5=E4=BB=B2=E5=A8=81=29?= Date: Wed, 2 Apr 2025 13:09:30 +0800 Subject: [PATCH 22/22] fix comments --- wren-ui/src/apollo/server/adaptors/wrenAIAdaptor.ts | 2 +- .../apollo/server/backgrounds/adjustmentBackgroundTracker.ts | 4 ++-- wren-ui/src/apollo/server/services/askingService.ts | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/wren-ui/src/apollo/server/adaptors/wrenAIAdaptor.ts b/wren-ui/src/apollo/server/adaptors/wrenAIAdaptor.ts index 21c747bed1..e0caa6503b 100644 --- a/wren-ui/src/apollo/server/adaptors/wrenAIAdaptor.ts +++ b/wren-ui/src/apollo/server/adaptors/wrenAIAdaptor.ts @@ -722,7 +722,7 @@ export class WrenAIAdaptor implements IWrenAIAdaptor { response: body.response?.map((result: any) => ({ sql: result.sql, - type: result.type.toUpperCase() as AskCandidateType, + type: result.type?.toUpperCase() as AskCandidateType, })) || [], traceId: body.trace_id, }; diff --git a/wren-ui/src/apollo/server/backgrounds/adjustmentBackgroundTracker.ts b/wren-ui/src/apollo/server/backgrounds/adjustmentBackgroundTracker.ts index 12fc45efc2..6db81fa906 100644 --- a/wren-ui/src/apollo/server/backgrounds/adjustmentBackgroundTracker.ts +++ b/wren-ui/src/apollo/server/backgrounds/adjustmentBackgroundTracker.ts @@ -3,7 +3,6 @@ import { AskFeedbackInput, AskFeedbackResult, AskFeedbackStatus, - ProjectConfigurations, } from '@server/models/adaptor'; import { AskingTask, @@ -45,13 +44,14 @@ export type CreateAdjustmentTaskInput = AskFeedbackInput & { threadId: number; question: string; originalThreadResponseId: number; + configurations: { language: string }; }; export type RerunAdjustmentTaskInput = { threadResponseId: number; threadId: number; projectId: number; - configurations: ProjectConfigurations; + configurations: { language: string }; }; export interface IAdjustmentBackgroundTaskTracker { diff --git a/wren-ui/src/apollo/server/services/askingService.ts b/wren-ui/src/apollo/server/services/askingService.ts index 184f4a58ee..055caec9e0 100644 --- a/wren-ui/src/apollo/server/services/askingService.ts +++ b/wren-ui/src/apollo/server/services/askingService.ts @@ -9,7 +9,6 @@ import { ChartStatus, ChartAdjustmentOption, WrenAILanguage, - ProjectConfigurations, } from '@server/models/adaptor'; import { IDeployService } from './deployService'; import { IProjectService } from './projectService'; @@ -187,7 +186,7 @@ export interface IAskingService { rerunAdjustThreadResponseAnswer( threadResponseId: number, projectId: number, - configurations: ProjectConfigurations, + configurations: { language: string }, ): Promise<{ queryId: string }>; getAdjustmentTask(taskId: string): Promise; getAdjustmentTaskById(id: number): Promise; @@ -1120,7 +1119,7 @@ export class AskingService implements IAskingService { public async rerunAdjustThreadResponseAnswer( threadResponseId: number, projectId: number, - configurations: ProjectConfigurations, + configurations: { language: string }, ): Promise<{ queryId: string }> { const threadResponse = await this.threadResponseRepository.findOneBy({ id: threadResponseId,