From c5cd69eb58294941b493617b7fa10cd06f434e80 Mon Sep 17 00:00:00 2001 From: Pauline Pham <33726902+Yutcam@users.noreply.github.com> Date: Wed, 20 Dec 2023 11:35:20 +0100 Subject: [PATCH 01/23] Update global_agent_helper_prompt.md (#2962) * Update global_agent_helper_prompt.md (Big) Update: _Folders _Personal Assistants * formatting --------- Co-authored-by: Henry Fontanier --- front/prompt/global_agent_helper_prompt.md | 96 ++++++++++++---------- 1 file changed, 53 insertions(+), 43 deletions(-) diff --git a/front/prompt/global_agent_helper_prompt.md b/front/prompt/global_agent_helper_prompt.md index 55be6e93697c..c1d7e36b6629 100644 --- a/front/prompt/global_agent_helper_prompt.md +++ b/front/prompt/global_agent_helper_prompt.md @@ -8,18 +8,22 @@ Make sure your answers are clear and straightforward. Double-check your answers ## What is Dust? -Dust is a platform powered by GPT4-Turbo, Claude 2.1, and Mistral. It's designed to help teams work better with AI. These AI assistants are there to enhance your understanding of any topic, increase productivity, and improve work processes. They can help with company questions, draft documents, or simplify complex tasks. +Dust is a platform powered by GPT4-Turbo, Claude 2.1, and Mistral. It's designed to help teams work better with AI. These AI assistants are there to enhance your understanding of any topic, increase productivity, and improve work processes. They can help with company questions, draft documents, or simplify complex tasks. You can create personal assistants and shared assistants with your team. ## General concepts -### Assistant +### Assistants Dust assistants are AI-powered agents that employ frontier models like GPT-4 Turbo, Mistral and Claude 2.1. You can use two types of assistants inside Dust. - Frontier model assistants: Advanced large-scale models like @gpt4 and @claude surpass existing technologies from major AI developers (OpenAI, Google DeepMind, etc.), handling a variety of tasks such as text recognition, summarization, translation, and content generation. -- Custom assistants: Tailored by Dust or in-house builders for niche needs, these can be enhanced or instructed specifically. They perform particular tasks, like @notion or @slack for interacting with synced documents, or aiding in SQL optimization, customer support, UX feedback, or specialized document creation. +- Custom assistants: created by Dust, yourself— personal assistants or builders— workspace assistants. Custom assistants package specific use cases, are powered by the LLM of your choice, and are instructed to complete specific tasks. @notion or @slack created by Dust interact with synced documents from those platforms. You can create a custom Data assistant for SQL query generation, customer support, UX feedback, or specialized document creation like a memo. -To illustrate, while @dust handles organizational questions, @help provides Dust support, @slack searches Slack, and @gpt4/@claude offers direct large language model access. Multiple assistants can be leveraged concurrently to tackle varied tasks. +To illustrate, while @dust handles organizational questions, @help provides Dust support, @slack searches Slack, and @gpt4/@claude offers direct large language model access. Multiple assistants can be chained to complete complex tasks. + +### Assistants Gallery + +From there, you can create your personal list of assistants. Click on `+Add` to add an assistant to your list. Assistants can be created by yourself, your teammates, or Dust. ### Conversation @@ -31,13 +35,13 @@ Create conversations for new topics to keep the assistants focused! ### Workspace -In Dust, a Workspace is where users talk with assistants and customize it for their team's needs. Admins manage data access, invite members, and set roles for better teamwork. +In Dust, a Workspace is where users work with assistants and customize them for themselves or their team's needs. Admins manage data access, invite members, and set roles for better teamwork. -### Connections & Data Sources +### Connections & Folders In Dust, "Connections" are integrated platforms like Notion or Slack where assistants pull data from, available only on paid plans. Admins decide which data assistants can access. -Data Sources are custom data sources created by builders to provide assistants with specific information unavailable inside Connections. Data Sources are available to all plans. +Folders are custom data sources created by builders to provide assistants with specific information unavailable inside Connections. Folders are available for all plans. ### Synchronizing @@ -55,18 +59,18 @@ LLM embedding converts text into numerical vectors, positioning similar phrases ### How to invite members to the workspace -As an Admin, invite members by going to `Admin` > `Workspace` > `Members`, then use Member list to invite by email or set a whitelisted domain in Invitation Link > Settings and share the link. +As an Admin, invite members by going to `⚙️` > `Members` then use the `Member list` to invite by email or set a whitelisted domain in Invitation Link > Settings and share the link. -After they join, assign roles: admin, builder, or user. +After members join, assign roles: admin, builder, or user. ### What are the users’ different roles? -**Users**: Use the assistants in the Workspace. +**Users**: Use the assistants and create personal assistants in the Workspace. **Builders**: Users plus: -- Build custom assistants with Dust tools and Data Sources. -- Add Data Sources to the Workspace. +- Build custom assistants with Dust tools and Folders. +- Add Folders to the Workspace. **Admins**: Builders plus: @@ -96,7 +100,7 @@ Advanced features to use the Dust Slackbot. - If you want to add Dust to a group direct message, [convert them to a private channel first](https://slack.com/intl/en-gb/help/articles/217555437-Convert-a-group-direct-message-to-a-private-channel). - Users can interact with any other assistants via Slack by summoning @dust and adding a tilde "~" right before the assistant's name- i.e., "@dust ~gpt4 Hello!" if you want to interact with @gpt4. -- Builders can link a custom assistant to a **public** Slack channel when creating or editing a custom assistant, the assistant will automatically be used every time Dust is called in the public channel. To do this, go to `Admin` > `Assistants` > `Create` or `Edit` > `Slack Integration`. You can't link an assistant to a private channel. +- Builders can link a custom assistant to a **public** Slack channel when creating or editing a custom assistant, the assistant will automatically be used every time Dust is called in the public channel. Go to `Assistants` > `Workspace assistants` > `New` or `Edit` > `Slack Integration` to do this. You can't link an assistant to a private channel; to use an assistant inside a private channel, use the syntax `@dust ~name of the assistant` . To export your @dust conversation history in Slack, remember that it's like exporting direct messages. You can only do this if you're an Owner or admin of a Slack Business+ or Enterprise Grid account. @@ -108,7 +112,7 @@ To export your @dust conversation history in Slack, remember that it's like expo Connections are available only for paid plans. -As an Admin, go to ️Admin > Connections > Select the desired Connection, click `Connect` > Authenticate your account, and select the data you wish to synchronize with Dust. +As an Admin, go to ️`⚙️` > `Connections` > Select the desired Connection, click `Connect` > Authenticate your account, and select the data you wish to synchronize with Dust. ##Slack @@ -120,7 +124,7 @@ To synchronize Notion pages, the admin can only select top-level pages. To add l **How to update Connections** -As an admin, ️Admin > `Connections` > Select the desired Connection, click `Manage` > `Edit permissions` > Explore, and either select or deselect the data you want to synchronize with Dust. +As an admin, ️ ️`⚙️` > `Connections` > Select the desired Connection, click `Manage` > `Add/Remove data` > Explore, and either select or deselect the data you want to synchronize with Dust. ### What are Connections' current limits? @@ -131,9 +135,9 @@ Github: Dust only gathers data from issues, discussions, and top-level pull requ ### How long does synchronizing new messages or documents created in one of my Connections takes? -Dust syncs quickly, usually in seconds or minutes. To check the last sync: +Dust syncs quickly, usually in seconds or minutes. To check the last sync as an admin: -- Go to `Admin` > `Connections`. +- Go to `Assitants` > `Connections`. - Look for "last sync ~ x s ago." To see if a document has synced and view its contents: @@ -142,18 +146,18 @@ To see if a document has synced and view its contents: ### How to add data that are not supported as a Connection by Dust -As a user, you can add your data to a connected platform like Notion or Google Drive. +As a user, you can add your data to a connected platform like Notion or Google Drive. Ask an admin to verify if the data you added are synchronized with Dust. -Admins/builders add a data source by: +Admins/builders can add a Folders by: -- Going to `Admin` > `Data Sources`. -- Clicking `Add a new Data Source`. +- Going to `Assistants`> `Folders`. +- Clicking `Add a new Folder`. - Naming it and adding a description (optional). - Clicking `create`. -### What are Data Sources' current limits? +### What are the documents and PDF current limits? -Documents up to 2MB can be uploaded manually via Data Sources. +Documents up to 10MB can be uploaded manually via Folders. ### **Does Dust use user and company data to train its models?** @@ -165,7 +169,9 @@ A 750KB plain text document could contain around 125,000 words, assuming an aver ### How to configure which data sources @dust has access to -To configure the @dust assistant, got to `Admin` > `Assistants` and click on the `Manage` button next to the @dust assistant. You'll be enable / disable @dust and select which data sources it has access to. +To configure the @dust assistant, got to `Assistants` > `Dust Assistants` and click on the `Manage` button next to the @dust assistant. You'll be enable / disable @dust and select which data sources it has access to. + +Think about @dust as your general assistant to explore all the data synchronized with Dust. Don’t expect 100% accurate answers but use Dust as a router to navigate your knowledge. ## Dust’s plans @@ -178,11 +184,11 @@ To configure the @dust assistant, got to `Admin` > `Assistants` and click on the ### **Dust Paid plans** -To get features like unlimited GPT-4 and Claude, connecting to Notion, Google Drive, GitHub, Slack, and using the Dust Slackbot, you need to upgrade to a paid plan. +You need to upgrade to a paid plan to get features like unlimited GPT-4 and Claude, connecting to Notion, Google Drive, GitHub, Slack, multiple members, and using the Dust Slackbot. Upgrade by: -- Going to `Admin` > `Subscription`. +- Going to `⚙️` > `Subscription`. - Choosing the Pro Plan. ### How to pay as a business? @@ -207,14 +213,14 @@ To manage your subscription: ### What can I use an assistant for? -A Dust assistant can answer questions and chat with you. Each one is different, so check their descriptions to see which one(s) to use. +Assistants can answer questions and chat with you. Think about your assistants as an extension of yourself to create content in a specific format. Each assistant is different, so check their descriptions to see which one(s) to use. Try to chain assistants to complete complex tasks. -- Use @dust for questions about your company; it uses GPT-4 and knows public data until September 2021. +- Use @dust for questions about your company; it uses GPT-4 and knows public data until April 2023. - Use @help for help with Dust features. - Use @slack to find info in Slack. - Use @gpt4 or @claude for tasks with the latest AI models. -You can combine assistants, like asking @dust for customer insights and then having @claude and @gpt4 help write a memo based on that info. +You can combine assistants, like asking @dust for customer insights and then having @claude and @gpt4 help write a memo based on that info. But the most important thing to make the most of Dust is creating custom assistants: personal assistants or shared assistants with your team. ### Technically, how do assistants work? @@ -237,20 +243,25 @@ Dust offers 3 types of assistants: ### How to search for assistants? -Admin and builders can filter custom assistants via the search bar on top of the Custom Assistants List in `Admin` > `Assistants` . +Users can search for assistants inside the `Assistant Gallery`. Admin and builders can filter custom assistants via the search bar on top of the Assistants List in `Assistants` . ## Custom assistants ### What are custom assistants? -Custom assistants are AI agents created by builders in your workspace. They are made to achieve specific tasks defined by builders. +Custom assistants are AI agents created by users and builders in your workspace. They are made to achieve specific tasks defined by builders. They can be personal or shared with the team. ### How to create a custom assistant? -To create a custom assistant— verify your are a builder: +To create a custom assistant: + +As a user, click on `>` next to `Assistants` and `+ Create an Assistant` . + +As a builder or admin: -1. Navigate to `Admin` > `Assistants` > `Create a new Assistant`. -2. Name your assistant (no spaces) and write a description to explain its purpose. +1. To create a personal assistant: Navigate to `Assistants` > `My Assistants` > `+New`. +2. To create a workspace assistant available to all members of the workspace: Navigate to `Assistants` > `Workspace Assistants` > `+New`. You can also duplicate existing assistants from the Assistants Gallery. +3. Name your assistant (no spaces) and write a description to explain its purpose. Setup involves: @@ -281,11 +292,10 @@ Tap 🤖 in the chat bar to manage and edit your custom assistants. or -1. Go to `Admin`. -2. Select `Assistants`. -3. Choose your assistant. -4. Click `Edit`. -5. Make necessary changes and `save` them. +1. Go to `Assistants`. +2. Choose your assistant. +3. Click `Edit`. +4. Make necessary changes and `save` them. ### Why chose GPT-4 or Claude 2? @@ -306,7 +316,7 @@ Table with results as of summer 2023, based on Ethan Mollick analysis - [oneusef ## Conversation -You can create conversations that involve both colleagues and several AI agents, each AI agent having a unique purpose and capability. +You can create conversations that involve both colleagues and several AI agents, each assistant having a unique purpose and capability. ### How do I access an assistant? @@ -314,12 +324,12 @@ To use an assistant: - Type your message and mention the assistant with "@", or click the robot icon 🤖 in the message bar. - Always start with "@" when calling an assistant, or it won't respond. -- In Slack, if enabled by your Admin, use the @dust assistant in the same way. -- For the Dust Slackbot, type "@dust ~gpt4" and your question to engage with @gpt4 for example. +- In Slack, if enabled by your admin, use the @dust assistant in the same way. +- For the Dust Slackbot, use the syntax "@dust ~gpt4" and your question to engage with @gpt4 for example. ### How do I talk to an assistant? -When engaging with assistants, you can involve multiple assistants within a single conversation to tackle complex tasks. Start with "@" followed by the assistant's name to initiate interaction with each one. For instance, you could use one assistant to gather information and another to help organize that information into a document. This multi-assistant approach allows for a more collaborative and comprehensive assistance strategy. +When engaging with assistants, we recommend using multiple assistants within a single conversation to tackle complex tasks. Start with "@" followed by the assistant's name to initiate interaction with each one. For instance, you could use one assistant to gather information and another to help organize that information into a document. This multi-assistant approach allows for a more collaborative and comprehensive assistance strategy. ### Can I share a conversation? From 2400e468cac65c76edf2675285ed8775b5334493 Mon Sep 17 00:00:00 2001 From: Stanislas Polu Date: Wed, 20 Dec 2023 12:32:25 +0100 Subject: [PATCH 02/23] x/evals: CoT-consensus better stats (#2971) * Better consensus computations * updated RESULTS --- x/spolu/research/evals/RESULTS.log | 115 +++++++++--------- x/spolu/research/evals/lib/algorithms.ts | 6 +- .../evals/lib/algorithms/CoTConsensus.ts | 92 +++++++++----- x/spolu/research/evals/stores/.gitignore | 1 + 4 files changed, 122 insertions(+), 92 deletions(-) diff --git a/x/spolu/research/evals/RESULTS.log b/x/spolu/research/evals/RESULTS.log index e08a6359bd56..063abcfa48d6 100644 --- a/x/spolu/research/evals/RESULTS.log +++ b/x/spolu/research/evals/RESULTS.log @@ -13,7 +13,6 @@ Finished run: algorithm=CoT dataset=Game24 provider=mistral model=mistral-medium Finished run: algorithm=CoT dataset=MATH provider=openai model=gpt-3.5-turbo-1106 check=51 total=128 Finished run: algorithm=CoT dataset=MATH provider=openai model=gpt-4-1106-preview check=88 total=128 - Finished run: algorithm=CoT dataset=MATH provider=mistral model=mistral-small check=39 total=128 Finished run: algorithm=CoT dataset=MATH provider=mistral model=mistral-medium check=49 total=128 @@ -22,75 +21,77 @@ Finished run: algorithm=CoT dataset=MATH provider=mistral model=mistral-medium c ## Game24 Finished run: algorithm=CoT-consensus dataset=Game24 provider=openai model=gpt-3.5-turbo-1106 -Result: algorithm=CoT-consensus poolSize=1 dataset=Game24 provider=openai model=gpt-3.5-turbo-1106 check=5 total=128 -Result: algorithm=CoT-consensus poolSize=2 dataset=Game24 provider=openai model=gpt-3.5-turbo-1106 check=5 total=128 -Result: algorithm=CoT-consensus poolSize=4 dataset=Game24 provider=openai model=gpt-3.5-turbo-1106 check=5 total=128 -Result: algorithm=CoT-consensus poolSize=8 dataset=Game24 provider=openai model=gpt-3.5-turbo-1106 check=7 total=128 -Result: algorithm=CoT-consensus poolSize=16 dataset=Game24 provider=openai model=gpt-3.5-turbo-1106 check=7 total=128 -Result: algorithm=CoT-consensus poolSize=32 dataset=Game24 provider=openai model=gpt-3.5-turbo-1106 check=10 total=128 -Final stats: rate=17.28/spromptTokensRate=12608.702/s completionTokensRate=891.42/s promptTokensTotal=2988275 completionTokensTotal=211267 +Result: algorithm=CoT-consensus poolSize=1 dataset=Game24 provider=openai model=gpt-3.5-turbo-1106 check=4.19 total=128.00 +Result: algorithm=CoT-consensus poolSize=2 dataset=Game24 provider=openai model=gpt-3.5-turbo-1106 check=4.69 total=128.00 +Result: algorithm=CoT-consensus poolSize=4 dataset=Game24 provider=openai model=gpt-3.5-turbo-1106 check=5.75 total=128.00 +Result: algorithm=CoT-consensus poolSize=8 dataset=Game24 provider=openai model=gpt-3.5-turbo-1106 check=7.00 total=128.00 +Result: algorithm=CoT-consensus poolSize=16 dataset=Game24 provider=openai model=gpt-3.5-turbo-1106 check=7.00 total=128.00 +Result: algorithm=CoT-consensus poolSize=32 dataset=Game24 provider=openai model=gpt-3.5-turbo-1106 check=10.00 total=128.00 +Final stats: rate=24.16/s promptTokensTotal=2988275 completionTokensTotal=211267 Finished run: algorithm=CoT-consensus dataset=Game24 provider=openai model=gpt-4-1106-preview -Result: algorithm=CoT-consensus poolSize=1 dataset=Game24 provider=openai model=gpt-4-1106-preview check=5 total=128 -Result: algorithm=CoT-consensus poolSize=2 dataset=Game24 provider=openai model=gpt-4-1106-preview check=5 total=128 -Result: algorithm=CoT-consensus poolSize=4 dataset=Game24 provider=openai model=gpt-4-1106-preview check=5 total=128 -Result: algorithm=CoT-consensus poolSize=8 dataset=Game24 provider=openai model=gpt-4-1106-preview check=6 total=128 -Result: algorithm=CoT-consensus poolSize=16 dataset=Game24 provider=openai model=gpt-4-1106-preview check=5 total=128 -Result: algorithm=CoT-consensus poolSize=32 dataset=Game24 provider=openai model=gpt-4-1106-preview check=4 total=128 -Final stats: rate=1.21/spromptTokensRate=885.027/s completionTokensRate=100.03/s promptTokensTotal=2988275 completionTokensTotal=337743 +Result: algorithm=CoT-consensus poolSize=1 dataset=Game24 provider=openai model=gpt-4-1106-preview check=6.06 total=128.00 +Result: algorithm=CoT-consensus poolSize=2 dataset=Game24 provider=openai model=gpt-4-1106-preview check=5.38 total=128.00 +Result: algorithm=CoT-consensus poolSize=4 dataset=Game24 provider=openai model=gpt-4-1106-preview check=4.88 total=128.00 +Result: algorithm=CoT-consensus poolSize=8 dataset=Game24 provider=openai model=gpt-4-1106-preview check=5.50 total=128.00 +Result: algorithm=CoT-consensus poolSize=16 dataset=Game24 provider=openai model=gpt-4-1106-preview check=4.50 total=128.00 +Result: algorithm=CoT-consensus poolSize=32 dataset=Game24 provider=openai model=gpt-4-1106-preview check=4.00 total=128.00 +Final stats: rate=24.38/s promptTokensTotal=2988275 completionTokensTotal=337743 Finished run: algorithm=CoT-consensus dataset=Game24 provider=mistral model=mistral-small -Result: algorithm=CoT-consensus poolSize=1 dataset=Game24 provider=mistral model=mistral-small check=2 total=128 -Result: algorithm=CoT-consensus poolSize=2 dataset=Game24 provider=mistral model=mistral-small check=2 total=128 -Result: algorithm=CoT-consensus poolSize=4 dataset=Game24 provider=mistral model=mistral-small check=2 total=128 -Result: algorithm=CoT-consensus poolSize=8 dataset=Game24 provider=mistral model=mistral-small check=1 total=128 -Result: algorithm=CoT-consensus poolSize=16 dataset=Game24 provider=mistral model=mistral-small check=0 total=128 -Result: algorithm=CoT-consensus poolSize=32 dataset=Game24 provider=mistral model=mistral-small check=0 total=128 -Final stats: rate=2.56/spromptTokensRate=2222.717/s completionTokensRate=221.41/s promptTokensTotal=3551411 completionTokensTotal=353772 +Result: algorithm=CoT-consensus poolSize=1 dataset=Game24 provider=mistral model=mistral-small check=1.56 total=128.00 +Result: algorithm=CoT-consensus poolSize=2 dataset=Game24 provider=mistral model=mistral-small check=1.50 total=128.00 +Result: algorithm=CoT-consensus poolSize=4 dataset=Game24 provider=mistral model=mistral-small check=1.50 total=128.00 +Result: algorithm=CoT-consensus poolSize=8 dataset=Game24 provider=mistral model=mistral-small check=1.00 total=128.00 +Result: algorithm=CoT-consensus poolSize=16 dataset=Game24 provider=mistral model=mistral-small check=0.50 total=128.00 +Result: algorithm=CoT-consensus poolSize=32 dataset=Game24 provider=mistral model=mistral-small check=0.00 total=128.00 +Final stats: rate=24.62/s promptTokensTotal=3551411 completionTokensTotal=353772 Finished run: algorithm=CoT-consensus dataset=Game24 provider=mistral model=mistral-medium -Result: algorithm=CoT-consensus poolSize=1 dataset=Game24 provider=mistral model=mistral-medium check=0 total=128 -Result: algorithm=CoT-consensus poolSize=2 dataset=Game24 provider=mistral model=mistral-medium check=0 total=128 -Result: algorithm=CoT-consensus poolSize=4 dataset=Game24 provider=mistral model=mistral-medium check=0 total=128 -Result: algorithm=CoT-consensus poolSize=8 dataset=Game24 provider=mistral model=mistral-medium check=0 total=128 -Result: algorithm=CoT-consensus poolSize=16 dataset=Game24 provider=mistral model=mistral-medium check=0 total=128 -Result: algorithm=CoT-consensus poolSize=32 dataset=Game24 provider=mistral model=mistral-medium check=0 total=128 -Final stats: rate=1.84/spromptTokensRate=1594.544/s completionTokensRate=178.56/s promptTokensTotal=3555507 completionTokensTotal=398154 +Result: algorithm=CoT-consensus poolSize=1 dataset=Game24 provider=mistral model=mistral-medium check=0.03 total=128.00 +Result: algorithm=CoT-consensus poolSize=2 dataset=Game24 provider=mistral model=mistral-medium check=0.00 total=128.00 +Result: algorithm=CoT-consensus poolSize=4 dataset=Game24 provider=mistral model=mistral-medium check=0.00 total=128.00 +Result: algorithm=CoT-consensus poolSize=8 dataset=Game24 provider=mistral model=mistral-medium check=0.00 total=128.00 +Result: algorithm=CoT-consensus poolSize=16 dataset=Game24 provider=mistral model=mistral-medium check=0.00 total=128.00 +Result: algorithm=CoT-consensus poolSize=32 dataset=Game24 provider=mistral model=mistral-medium check=0.00 total=128.00 +Final stats: rate=24.27/s promptTokensTotal=3555507 completionTokensTotal=398154 ## MATH Finished run: algorithm=CoT-consensus dataset=MATH provider=openai model=gpt-3.5-turbo-1106 -Result: algorithm=CoT-consensus poolSize=1 dataset=MATH provider=openai model=gpt-3.5-turbo-1106 check=55 total=128 -Result: algorithm=CoT-consensus poolSize=2 dataset=MATH provider=openai model=gpt-3.5-turbo-1106 check=55 total=128 -Result: algorithm=CoT-consensus poolSize=4 dataset=MATH provider=openai model=gpt-3.5-turbo-1106 check=63 total=128 -Result: algorithm=CoT-consensus poolSize=8 dataset=MATH provider=openai model=gpt-3.5-turbo-1106 check=66 total=128 -Result: algorithm=CoT-consensus poolSize=16 dataset=MATH provider=openai model=gpt-3.5-turbo-1106 check=70 total=128 -Result: algorithm=CoT-consensus poolSize=32 dataset=MATH provider=openai model=gpt-3.5-turbo-1106 check=68 total=128 -Final stats: rate=3.33/s promptTokensRate=7484.167/s completionTokensRate=606.85/s promptTokensTotal=9267718 completionTokensTotal=802822 +Result: algorithm=CoT-consensus poolSize=1 dataset=MATH provider=openai model=gpt-3.5-turbo-1106 check=52.69 total=128.00 +Result: algorithm=CoT-consensus poolSize=2 dataset=MATH provider=openai model=gpt-3.5-turbo-1106 check=52.38 total=128.00 +Result: algorithm=CoT-consensus poolSize=4 dataset=MATH provider=openai model=gpt-3.5-turbo-1106 check=61.50 total=128.00 +Result: algorithm=CoT-consensus poolSize=8 dataset=MATH provider=openai model=gpt-3.5-turbo-1106 check=66.00 total=128.00 +Result: algorithm=CoT-consensus poolSize=16 dataset=MATH provider=openai model=gpt-3.5-turbo-1106 check=68.00 total=128.00 +Result: algorithm=CoT-consensus poolSize=32 dataset=MATH provider=openai model=gpt-3.5-turbo-1106 check=68.00 total=128.00 +Final stats: promptTokensTotal=9267718 completionTokensTotal=802822 Finished run: algorithm=CoT-consensus dataset=MATH provider=openai model=gpt-4-1106-preview -Result: algorithm=CoT-consensus poolSize=1 dataset=MATH provider=openai model=gpt-4-1106-preview check=80 total=128 -Result: algorithm=CoT-consensus poolSize=2 dataset=MATH provider=openai model=gpt-4-1106-preview check=80 total=128 -Result: algorithm=CoT-consensus poolSize=4 dataset=MATH provider=openai model=gpt-4-1106-preview check=83 total=128 -Result: algorithm=CoT-consensus poolSize=8 dataset=MATH provider=openai model=gpt-4-1106-preview check=91 total=128 -Result: algorithm=CoT-consensus poolSize=16 dataset=MATH provider=openai model=gpt-4-1106-preview check=92 total=128 -Result: algorithm=CoT-consensus poolSize=32 dataset=MATH provider=openai model=gpt-4-1106-preview check=91 total=128 -Final stats: rate=0.44/spromptTokensRate=989.536/s completionTokensRate=119.40/s promptTokensTotal=9267718 completionTokensTotal=1118264 +Result: algorithm=CoT-consensus poolSize=1 dataset=MATH provider=openai model=gpt-4-1106-preview check=80.78 total=128.00 +Result: algorithm=CoT-consensus poolSize=2 dataset=MATH provider=openai model=gpt-4-1106-preview check=81.50 total=128.00 +Result: algorithm=CoT-consensus poolSize=4 dataset=MATH provider=openai model=gpt-4-1106-preview check=85.50 total=128.00 +Result: algorithm=CoT-consensus poolSize=8 dataset=MATH provider=openai model=gpt-4-1106-preview check=90.00 total=128.00 +Result: algorithm=CoT-consensus poolSize=16 dataset=MATH provider=openai model=gpt-4-1106-preview check=92.00 total=128.00 +Result: algorithm=CoT-consensus poolSize=32 dataset=MATH provider=openai model=gpt-4-1106-preview check=91.00 total=128.00 +Final stats: promptTokensTotal=9267718 completionTokensTotal=1118264 Finished run: algorithm=CoT-consensus dataset=MATH provider=mistral model=mistral-small -Result: algorithm=CoT-consensus poolSize=1 dataset=MATH provider=mistral model=mistral-small check=36 total=128 -Result: algorithm=CoT-consensus poolSize=2 dataset=MATH provider=mistral model=mistral-small check=36 total=128 -Result: algorithm=CoT-consensus poolSize=4 dataset=MATH provider=mistral model=mistral-small check=42 total=128 -Result: algorithm=CoT-consensus poolSize=8 dataset=MATH provider=mistral model=mistral-small check=51 total=128 -Result: algorithm=CoT-consensus poolSize=16 dataset=MATH provider=mistral model=mistral-small check=59 total=128 -Result: algorithm=CoT-consensus poolSize=32 dataset=MATH provider=mistral model=mistral-small check=60 total=128 -Final stats: rate=2.59/spromptTokensRate=6575.136/s completionTokensRate=617.24/s promptTokensTotal=10387169 completionTokensTotal=975095 +Result: algorithm=CoT-consensus poolSize=1 dataset=MATH provider=mistral model=mistral-small check=37.59 total=128.00 +Result: algorithm=CoT-consensus poolSize=2 dataset=MATH provider=mistral model=mistral-small check=38.69 total=128.00 +Result: algorithm=CoT-consensus poolSize=4 dataset=MATH provider=mistral model=mistral-small check=46.38 total=128.00 +Result: algorithm=CoT-consensus poolSize=8 dataset=MATH provider=mistral model=mistral-small check=53.50 total=128.00 +Result: algorithm=CoT-consensus poolSize=16 dataset=MATH provider=mistral model=mistral-small check=58.50 total=128.00 +Result: algorithm=CoT-consensus poolSize=32 dataset=MATH provider=mistral model=mistral-small check=60.00 total=128.00 +Final stats: promptTokensTotal=10387169 completionTokensTotal=975095 Finished run: algorithm=CoT-consensus dataset=MATH provider=mistral model=mistral-medium -Result: algorithm=CoT-consensus poolSize=1 dataset=MATH provider=mistral model=mistral-medium check=41 total=128 -Result: algorithm=CoT-consensus poolSize=2 dataset=MATH provider=mistral model=mistral-medium check=41 total=128 -Result: algorithm=CoT-consensus poolSize=4 dataset=MATH provider=mistral model=mistral-medium check=50 total=128 -Result: algorithm=CoT-consensus poolSize=8 dataset=MATH provider=mistral model=mistral-medium check=56 total=128 -Result: algorithm=CoT-consensus poolSize=16 dataset=MATH provider=mistral model=mistral-medium check=64 total=128 -Result: algorithm=CoT-consensus poolSize=32 dataset=MATH provider=mistral model=mistral-medium check=68 total=128 -Final stats: rate=0.96/spromptTokensRate=2407.524/s completionTokensRate=255.92/s promptTokensTotal=10323371 completionTokensTotal=1097376 +Result: algorithm=CoT-consensus poolSize=1 dataset=MATH provider=mistral model=mistral-medium check=42.72 total=128.00 +Result: algorithm=CoT-consensus poolSize=2 dataset=MATH provider=mistral model=mistral-medium check=41.69 total=128.00 +Result: algorithm=CoT-consensus poolSize=4 dataset=MATH provider=mistral model=mistral-medium check=52.13 total=128.00 +Result: algorithm=CoT-consensus poolSize=8 dataset=MATH provider=mistral model=mistral-medium check=59.75 total=128.00 +Result: algorithm=CoT-consensus poolSize=16 dataset=MATH provider=mistral model=mistral-medium check=62.50 total=128.00 +Result: algorithm=CoT-consensus poolSize=32 dataset=MATH provider=mistral model=mistral-medium check=68.00 total=128.00 +Final stats: promptTokensTotal=10323371 completionTokensTotal=1097376 + + diff --git a/x/spolu/research/evals/lib/algorithms.ts b/x/spolu/research/evals/lib/algorithms.ts index eca39e6f1ab1..62fd90db36ee 100644 --- a/x/spolu/research/evals/lib/algorithms.ts +++ b/x/spolu/research/evals/lib/algorithms.ts @@ -168,14 +168,10 @@ export abstract class Algorithm { (acc, x) => acc + x.completion.usage.promptTokens, 0 ); - const completionTokensRate = completionTokensTotal / (duration / 1000); - const promptTokensRate = promptTokensTotal / (duration / 1000); console.log( `Final stats: ` + - `rate=${rate.toFixed(2)}/s` + - `promptTokensRate=${promptTokensRate.toFixed(3)}/s ` + - `completionTokensRate=${completionTokensRate.toFixed(2)}/s ` + + `rate=${rate.toFixed(2)}/s ` + `promptTokensTotal=${promptTokensTotal} ` + `completionTokensTotal=${completionTokensTotal}` ); diff --git a/x/spolu/research/evals/lib/algorithms/CoTConsensus.ts b/x/spolu/research/evals/lib/algorithms/CoTConsensus.ts index 996958a4cb47..e91fe8266bb9 100644 --- a/x/spolu/research/evals/lib/algorithms/CoTConsensus.ts +++ b/x/spolu/research/evals/lib/algorithms/CoTConsensus.ts @@ -17,33 +17,39 @@ export class CoTConsensus extends CoT { return "CoT-consensus"; } - resultFromPool(poolSize: number, test: Test): TestResult { - const answers: { [key: string]: { check: boolean; count: number } } = {}; + resultFromPool(poolSize: number, test: Test): TestResult[] { + const results: TestResult[] = []; - for (const result of this.poolResults[test.id].slice(0, poolSize)) { - if (!answers[result.answer]) { - answers[result.answer] = { check: result.check, count: 0 }; + for (let i = 1; i * poolSize <= this.poolResults[test.id].length; i++) { + const answers: { [key: string]: { check: boolean; count: number } } = {}; + + const pool = this.poolResults[test.id].slice( + (i - 1) * poolSize, + i * poolSize + ); + + for (const result of pool) { + if (!answers[result.answer]) { + answers[result.answer] = { check: result.check, count: 0 }; + } + answers[result.answer].count++; } - answers[result.answer].count++; - } - // find the max count - let maxCount = 0; - let maxAnswer = ""; - let maxCheck = false; - for (const answer in answers) { - if (answers[answer].count > maxCount) { - maxCount = answers[answer].count; - maxAnswer = answer; - maxCheck = answers[answer].check; + // find the max count + let maxCount = 0; + let maxAnswer = ""; + let maxCheck = false; + for (const answer in answers) { + if (answers[answer].count > maxCount) { + maxCount = answers[answer].count; + maxAnswer = answer; + maxCheck = answers[answer].check; + } } + results.push({ test, answer: maxAnswer, check: maxCheck }); } - return { - test, - answer: maxAnswer, - check: maxCheck, - }; + return results; } async runOne({ @@ -60,30 +66,56 @@ export class CoTConsensus extends CoT { } this.poolResults[test.id].push(result); } - - return this.resultFromPool(this.VOTE_COUNT, test); + return this.resultFromPool(this.VOTE_COUNT, test)[0]; } computeResults(): void { for (let p = 1; p <= this.VOTE_COUNT; p = p * 2) { - let check = 0; - let total = 0; + const pools: TestResult[][] = []; + for (let i = 0; i < this.VOTE_COUNT / p; i++) { + pools.push([]); + } for (const testId in this.poolResults) { const test = this.poolResults[testId][0].test; - const result = this.resultFromPool(p, test); - total++; - if (result.check) { - check++; + const results = this.resultFromPool(p, test); + + if (results.length !== pools.length) { + throw new Error( + `Expected ${pools.length} pools, got ${results.length}` + ); + } + + for (let i = 0; i < results.length; i++) { + pools[i].push(results[i]); + } + } + + const check = []; + const total = []; + + for (const pool of pools) { + let checkCount = 0; + let totalCount = 0; + for (const result of pool) { + if (result.check) { + checkCount++; + } + totalCount++; } + check.push(checkCount); + total.push(totalCount); } + const checkAvg = check.reduce((a, b) => a + b, 0) / check.length; + const totalAvg = total.reduce((a, b) => a + b, 0) / total.length; + console.log( `Result: algorithm=${this.algorithm()} poolSize=${p} dataset=${ this.dataset.dataset } ` + `provider=${this.model.provider} model=${this.model.model()} ` + - `check=${check} total=${total}` + `check=${checkAvg.toFixed(2)} total=${totalAvg.toFixed(2)}` ); } } diff --git a/x/spolu/research/evals/stores/.gitignore b/x/spolu/research/evals/stores/.gitignore index 9b1dffd90fdc..97fc976772ab 100644 --- a/x/spolu/research/evals/stores/.gitignore +++ b/x/spolu/research/evals/stores/.gitignore @@ -1 +1,2 @@ *.sqlite +*.sqlite-journal From b720bd7493bfb5b18d7bb8925fb08a05b6c12b56 Mon Sep 17 00:00:00 2001 From: Philippe Rolet Date: Wed, 20 Dec 2023 12:54:40 +0100 Subject: [PATCH 03/23] [Runner - Fix] Fix deploy broken by shared back/front libs (#2972) * [Runner - Fix] Fix deploy broken by shared back/front libs Related [discussion](https://dust4ai.slack.com/archives/C050SM8NSPK/p1703068650076399) Backend code was moved to lib/api * removed check for sendgrid api key --- front/lib/api/data_sources.ts | 106 ++++++++++++++++- front/lib/data_sources.ts | 111 +----------------- front/lib/email.ts | 5 +- .../[wId]/data_sources/[name]/index.ts | 2 +- front/pages/api/stripe/webhook.ts | 2 +- 5 files changed, 109 insertions(+), 117 deletions(-) diff --git a/front/lib/api/data_sources.ts b/front/lib/api/data_sources.ts index ed319600b1e9..18952115e72e 100644 --- a/front/lib/api/data_sources.ts +++ b/front/lib/api/data_sources.ts @@ -1,8 +1,21 @@ -import { DataSourceType } from "@dust-tt/types"; +import { + APIError, + ConnectorProvider, + ConnectorsAPI, + CoreAPI, + DataSourceType, + Err, + Ok, + Result, +} from "@dust-tt/types"; import { Op } from "sequelize"; +import { getMembers } from "@app/lib/api/workspace"; import { Authenticator } from "@app/lib/auth"; +import { sendGithubDeletionEmail } from "@app/lib/email"; import { DataSource } from "@app/lib/models"; +import logger from "@app/logger/logger"; +import { launchScrubDataSourceWorkflow } from "@app/poke/temporal/client"; export async function getDataSource( auth: Authenticator, @@ -83,3 +96,94 @@ export async function getDataSources( }; }); } +export async function deleteDataSource( + auth: Authenticator, + dataSourceName: string +): Promise> { + const workspace = auth.workspace(); + if (!workspace) { + return new Err({ + type: "workspace_not_found", + message: "Could not find the workspace.", + }); + } + if (!auth.isAdmin()) { + return new Err({ + type: "workspace_auth_error", + message: + "Only users that are `admins` for the current workspace can delete data sources.", + }); + } + const dataSource = await DataSource.findOne({ + where: { + workspaceId: workspace.id, + name: dataSourceName, + }, + }); + if (!dataSource) { + return new Err({ + type: "data_source_not_found", + message: "Could not find the data source.", + }); + } + + const dustAPIProjectId = dataSource.dustAPIProjectId; + + const connectorsAPI = new ConnectorsAPI(logger); + if (dataSource.connectorId) { + const connDeleteRes = await connectorsAPI.deleteConnector( + dataSource.connectorId.toString(), + true + ); + if (connDeleteRes.isErr()) { + // If we get a not found we proceed with the deletion of the data source. This will enable + // us to retry deletion of the data source if it fails at the Core level. + if (connDeleteRes.error.error.type !== "connector_not_found") { + return new Err({ + type: "internal_server_error", + message: `Error deleting connector: ${connDeleteRes.error.error.message}`, + }); + } + } + } + + const coreAPI = new CoreAPI(logger); + const coreDeleteRes = await coreAPI.deleteDataSource({ + projectId: dustAPIProjectId, + dataSourceName: dataSource.name, + }); + if (coreDeleteRes.isErr()) { + return new Err({ + type: "internal_server_error", + message: `Error deleting core data source: ${coreDeleteRes.error.message}`, + }); + } + + await dataSource.destroy(); + + await launchScrubDataSourceWorkflow({ + wId: workspace.sId, + dustAPIProjectId, + }); + if (dataSource.connectorProvider) + await warnPostDeletion(auth, dataSource.connectorProvider); + + return new Ok({ success: true }); +} + +async function warnPostDeletion( + auth: Authenticator, + dataSourceProvider: ConnectorProvider +) { + // if the datasource is Github, send an email inviting to delete the Github app + switch (dataSourceProvider) { + case "github": + // get admin emails + const adminEmails = (await getMembers(auth, "admin")).map((u) => u.email); + // send email to admins + for (const email of adminEmails) await sendGithubDeletionEmail(email); + break; + default: + break; + } +} diff --git a/front/lib/data_sources.ts b/front/lib/data_sources.ts index 285db818a6de..96876775282b 100644 --- a/front/lib/data_sources.ts +++ b/front/lib/data_sources.ts @@ -1,21 +1,4 @@ -import { - APIError, - ConnectorProvider, - ConnectorsAPI, - CoreAPI, - CoreAPIDocument, - DataSourceType, - Err, - Ok, - Result, -} from "@dust-tt/types"; - -import { getMembers } from "@app/lib/api/workspace"; -import { Authenticator } from "@app/lib/auth"; -import { sendGithubDeletionEmail } from "@app/lib/email"; -import { DataSource } from "@app/lib/models"; -import logger from "@app/logger/logger"; -import { launchScrubDataSourceWorkflow } from "@app/poke/temporal/client"; +import { CoreAPIDocument, DataSourceType } from "@dust-tt/types"; export function getProviderLogoPathForDataSource( ds: DataSourceType @@ -57,95 +40,3 @@ export function getDisplayNameForDocument(document: CoreAPIDocument): string { } return titleTag.substring(titleTagPrefix.length); } - -export async function deleteDataSource( - auth: Authenticator, - dataSourceName: string -): Promise> { - const workspace = auth.workspace(); - if (!workspace) { - return new Err({ - type: "workspace_not_found", - message: "Could not find the workspace.", - }); - } - if (!auth.isAdmin()) { - return new Err({ - type: "workspace_auth_error", - message: - "Only users that are `admins` for the current workspace can delete data sources.", - }); - } - const dataSource = await DataSource.findOne({ - where: { - workspaceId: workspace.id, - name: dataSourceName, - }, - }); - if (!dataSource) { - return new Err({ - type: "data_source_not_found", - message: "Could not find the data source.", - }); - } - - const dustAPIProjectId = dataSource.dustAPIProjectId; - - const connectorsAPI = new ConnectorsAPI(logger); - if (dataSource.connectorId) { - const connDeleteRes = await connectorsAPI.deleteConnector( - dataSource.connectorId.toString(), - true - ); - if (connDeleteRes.isErr()) { - // If we get a not found we proceed with the deletion of the data source. This will enable - // us to retry deletion of the data source if it fails at the Core level. - if (connDeleteRes.error.error.type !== "connector_not_found") { - return new Err({ - type: "internal_server_error", - message: `Error deleting connector: ${connDeleteRes.error.error.message}`, - }); - } - } - } - - const coreAPI = new CoreAPI(logger); - const coreDeleteRes = await coreAPI.deleteDataSource({ - projectId: dustAPIProjectId, - dataSourceName: dataSource.name, - }); - if (coreDeleteRes.isErr()) { - return new Err({ - type: "internal_server_error", - message: `Error deleting core data source: ${coreDeleteRes.error.message}`, - }); - } - - await dataSource.destroy(); - - await launchScrubDataSourceWorkflow({ - wId: workspace.sId, - dustAPIProjectId, - }); - if (dataSource.connectorProvider) - await warnPostDeletion(auth, dataSource.connectorProvider); - - return new Ok({ success: true }); -} - -async function warnPostDeletion( - auth: Authenticator, - dataSourceProvider: ConnectorProvider -) { - // if the datasource is Github, send an email inviting to delete the Github app - switch (dataSourceProvider) { - case "github": - // get admin emails - const adminEmails = (await getMembers(auth, "admin")).map((u) => u.email); - // send email to admins - for (const email of adminEmails) await sendGithubDeletionEmail(email); - break; - default: - break; - } -} diff --git a/front/lib/email.ts b/front/lib/email.ts index 37a6bbd0bfa8..ce21d1685290 100644 --- a/front/lib/email.ts +++ b/front/lib/email.ts @@ -7,11 +7,8 @@ import sgMail from "@sendgrid/mail"; import { XP1User } from "@app/lib/models"; import logger from "@app/logger/logger"; -const { SENDGRID_API_KEY, XP1_CHROME_WEB_STORE_URL } = process.env; +const { SENDGRID_API_KEY = "", XP1_CHROME_WEB_STORE_URL } = process.env; -if (!SENDGRID_API_KEY) { - throw new Error("Missing SENDGRID_API_KEY env variable"); -} sgMail.setApiKey(SENDGRID_API_KEY); export async function sendEmail(email: string, message: any) { diff --git a/front/pages/api/poke/workspaces/[wId]/data_sources/[name]/index.ts b/front/pages/api/poke/workspaces/[wId]/data_sources/[name]/index.ts index 8eb863ca3514..b3257005a374 100644 --- a/front/pages/api/poke/workspaces/[wId]/data_sources/[name]/index.ts +++ b/front/pages/api/poke/workspaces/[wId]/data_sources/[name]/index.ts @@ -1,8 +1,8 @@ import { ReturnedAPIErrorType } from "@dust-tt/types"; import { NextApiRequest, NextApiResponse } from "next"; +import { deleteDataSource } from "@app/lib/api/data_sources"; import { Authenticator, getSession } from "@app/lib/auth"; -import { deleteDataSource } from "@app/lib/data_sources"; import { apiError, withLogging } from "@app/logger/withlogging"; export type DeleteDataSourceResponseBody = { diff --git a/front/pages/api/stripe/webhook.ts b/front/pages/api/stripe/webhook.ts index e8794c461804..d3ab0f3a483a 100644 --- a/front/pages/api/stripe/webhook.ts +++ b/front/pages/api/stripe/webhook.ts @@ -13,9 +13,9 @@ import { getAgentConfigurations, } from "@app/lib/api/assistant/configuration"; import { getDataSources } from "@app/lib/api/data_sources"; +import { deleteDataSource } from "@app/lib/api/data_sources"; import { getMembers } from "@app/lib/api/workspace"; import { Authenticator } from "@app/lib/auth"; -import { deleteDataSource } from "@app/lib/data_sources"; import { front_sequelize } from "@app/lib/databases"; import { sendAdminDowngradeTooMuchDataEmail, From 3c3c27620d6a156739e49edbe73d69b3a8501fed Mon Sep 17 00:00:00 2001 From: Philippe Rolet Date: Wed, 20 Dec 2023 13:16:12 +0100 Subject: [PATCH 04/23] [Runner] Avatars can be seen in membership list & member detail (#2944) * [Runner] Avatars can be seen in membership list & member detail Related [card](https://github.com/dust-tt/tasks/issues/289) and [discussion](https://dust4ai.slack.com/archives/C050SM8NSPK/p1702990922381299?thread_ts=1702899295.554769&cid=C050SM8NSPK) * switch back to message context --- front/lib/api/workspace.ts | 2 +- front/lib/auth.ts | 15 +++- front/lib/models/user.ts | 5 ++ .../migrations/20231219_imageUrl_backfill.ts | 84 +++++++++++++++++++ 4 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 front/migrations/20231219_imageUrl_backfill.ts diff --git a/front/lib/api/workspace.ts b/front/lib/api/workspace.ts index 7ab471d11e0b..0cdcd8046a69 100644 --- a/front/lib/api/workspace.ts +++ b/front/lib/api/workspace.ts @@ -124,7 +124,7 @@ export async function getMembers( fullName: u.firstName + (u.lastName ? ` ${u.lastName}` : ""), firstName: u.firstName, lastName: u.lastName, - image: null, + image: u.imageUrl, workspaces: [{ ...owner, role }], }; }); diff --git a/front/lib/auth.ts b/front/lib/auth.ts index 16dc6d4b9823..6020c3c24a1a 100644 --- a/front/lib/auth.ts +++ b/front/lib/auth.ts @@ -404,6 +404,19 @@ export async function getUserFromSession( }, }); + if (session.user.image !== user.imageUrl) { + void User.update( + { + imageUrl: session.user.image, + }, + { + where: { + id: user.id, + }, + } + ); + } + return { id: user.id, provider: user.provider, @@ -413,7 +426,7 @@ export async function getUserFromSession( firstName: user.firstName, lastName: user.lastName, fullName: user.firstName + (user.lastName ? ` ${user.lastName}` : ""), - image: session.user ? session.user.image : null, + image: user.imageUrl, workspaces: workspaces.map((w) => { const m = memberships.find((m) => m.workspaceId === w.id); let role = "none" as RoleType; diff --git a/front/lib/models/user.ts b/front/lib/models/user.ts index b1b445895cfc..a3c5e758e7cc 100644 --- a/front/lib/models/user.ts +++ b/front/lib/models/user.ts @@ -23,6 +23,7 @@ export class User extends Model< declare name: string; declare firstName: string; declare lastName: string | null; + declare imageUrl: string | null; declare isDustSuperUser: CreationOptional; } @@ -71,6 +72,10 @@ User.init( type: DataTypes.STRING, allowNull: true, }, + imageUrl: { + type: DataTypes.STRING, + allowNull: true, + }, isDustSuperUser: { type: DataTypes.BOOLEAN, defaultValue: false, diff --git a/front/migrations/20231219_imageUrl_backfill.ts b/front/migrations/20231219_imageUrl_backfill.ts new file mode 100644 index 000000000000..a2835627123b --- /dev/null +++ b/front/migrations/20231219_imageUrl_backfill.ts @@ -0,0 +1,84 @@ +import { + AgentConfiguration, + Membership, + User, + UserMessage, +} from "@app/lib/models"; + +async function main() { + console.log("Starting imageUrl backfill"); + const workspaceIds = ( + await AgentConfiguration.findAll({ + attributes: ["workspaceId"], + group: ["workspaceId"], + }) + ).map((a) => a.workspaceId); + + console.log(`Found ${workspaceIds.length} workspaces to update`); + const chunks = []; + for (let i = 0; i < workspaceIds.length; i += 16) { + chunks.push(workspaceIds.slice(i, i + 16)); + } + + for (let i = 0; i < chunks.length; i++) { + console.log(`Processing workspace chunk ${i}/${chunks.length}...`); + const chunk = chunks[i]; + + await Promise.all( + chunk.map((wid: number) => { + return (async () => { + await backfillImageUrl(wid); + })(); + }) + ); + } +} + +async function backfillImageUrl(workspaceId: number) { + // get all users from workspace whose imageUrl is null + const users = await User.findAll({ + where: { + imageUrl: null, + }, + include: [ + { + model: Membership, + where: { + workspaceId, + }, + required: true, + }, + ], + }); + + // for each user, find the last usermessage + // and set the user's imageUrl to the usermessage's userContextProfilePictureUrl + for (const user of users) { + const userMessage = await UserMessage.findOne({ + where: { + userId: user.id, + }, + order: [["createdAt", "DESC"]], + }); + if (!userMessage) { + console.log( + `No user messages found for user with id ${user.id} in workspace with id ${workspaceId}` + ); + continue; + } + + await user.update({ + imageUrl: userMessage.userContextProfilePictureUrl, + }); + } +} + +main() + .then(() => { + console.log("Done"); + process.exit(0); + }) + .catch((err) => { + console.error(err); + process.exit(1); + }); From 6240866f5cf8a50efd0a7f7653e3702f62cfa99e Mon Sep 17 00:00:00 2001 From: Philippe Rolet Date: Wed, 20 Dec 2023 14:39:24 +0100 Subject: [PATCH 05/23] [Runner] migration for imageUrl backfill (#2974) --- front/migrations/20231219_imageUrl_backfill.ts | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/front/migrations/20231219_imageUrl_backfill.ts b/front/migrations/20231219_imageUrl_backfill.ts index a2835627123b..995eb9298f9a 100644 --- a/front/migrations/20231219_imageUrl_backfill.ts +++ b/front/migrations/20231219_imageUrl_backfill.ts @@ -1,18 +1,12 @@ -import { - AgentConfiguration, - Membership, - User, - UserMessage, -} from "@app/lib/models"; +import { Membership, User, UserMessage, Workspace } from "@app/lib/models"; async function main() { console.log("Starting imageUrl backfill"); const workspaceIds = ( - await AgentConfiguration.findAll({ - attributes: ["workspaceId"], - group: ["workspaceId"], + await Workspace.findAll({ + attributes: ["id"], }) - ).map((a) => a.workspaceId); + ).map((a) => a.id); console.log(`Found ${workspaceIds.length} workspaces to update`); const chunks = []; From bc469a0a1886cb62ffda5f5e075345e3bd6b55e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daphn=C3=A9=20Popin?= Date: Wed, 20 Dec 2023 15:38:17 +0100 Subject: [PATCH 06/23] Agent DbQuery action: improve display of streamed events (#2973) --- .../assistant/conversation/AgentMessage.tsx | 1 + .../conversation/DatabaseQueryAction.tsx | 139 ++++++++++++------ .../api/assistant/actions/database_query.ts | 34 ++++- front/lib/api/assistant/agent.ts | 1 + front/lib/api/assistant/conversation.ts | 2 + front/lib/api/assistant/pubsub.ts | 2 + .../api/assistant/actions/database_query.ts | 8 + types/src/front/lib/api/assistant/agent.ts | 8 +- 8 files changed, 144 insertions(+), 51 deletions(-) diff --git a/front/components/assistant/conversation/AgentMessage.tsx b/front/components/assistant/conversation/AgentMessage.tsx index c436eb9533d2..66e8f36ef9e7 100644 --- a/front/components/assistant/conversation/AgentMessage.tsx +++ b/front/components/assistant/conversation/AgentMessage.tsx @@ -128,6 +128,7 @@ export function AgentMessage({ case "dust_app_run_params": case "dust_app_run_block": case "database_query_params": + case "database_query_output": setStreamedAgentMessage((m) => { return { ...m, action: event.action }; }); diff --git a/front/components/assistant/conversation/DatabaseQueryAction.tsx b/front/components/assistant/conversation/DatabaseQueryAction.tsx index 8c814bcad87b..f5ca39f8fa61 100644 --- a/front/components/assistant/conversation/DatabaseQueryAction.tsx +++ b/front/components/assistant/conversation/DatabaseQueryAction.tsx @@ -4,6 +4,7 @@ import { Chip, Icon, Spinner, + Tooltip, } from "@dust-tt/sparkle"; import { DatabaseQueryActionType } from "@dust-tt/types"; import dynamic from "next/dynamic"; @@ -20,72 +21,114 @@ export default function DatabaseQueryAction({ }: { databaseQueryAction: DatabaseQueryActionType; }) { - const [outputVisible, setOutputVisible] = useState(false); + const [isOutputExpanded, setIsOutputExpanded] = useState(false); + // Extracting question from the params + const params = databaseQueryAction.params; + const question = + typeof params?.question === "string" ? params.question : null; + + // Extracting query and result from the output const output = databaseQueryAction.output; - const query = output?.query; + const query = typeof output?.query === "string" ? output.query : null; + const noQuery = output?.no_query === true; + const results = output?.results; + + const isQueryStepCompleted = noQuery || query; + const isOutputStepCompleted = noQuery || (query && results); + + const trimText = (text: string, maxLength = 20) => { + const t = text.replaceAll("\n", " "); + return t.length > maxLength ? t.substring(0, maxLength) + "..." : t; + }; return ( <> -
-
- {!output ? ( -
- -
- ) : ( + {question && ( +
+
+
Question:
+
+ + + +
+ )} + + {!isQueryStepCompleted && ( +
+
+ Generating query... +
+ +
+ )} + + {isQueryStepCompleted && ( +
+
- {query ? "Query Executed:" : "Result: "} + Query:
- )} -
- {!!output && ( +
{ - setOutputVisible(!outputVisible); + setIsOutputExpanded(!isOutputExpanded); }} > - {query ? query : "No query generated, expand to see why"} - + {query ? query : "No query generated"} + {(noQuery || results) && ( + + )}
- )} - {outputVisible && ( -
- - {JSON.stringify(output, null, 2)} - + {isOutputExpanded && ( +
+ + {JSON.stringify(output, null, 2)} + +
+ )} +
+ )} + {isQueryStepCompleted && !isOutputStepCompleted && ( +
+
+ Running query...
- )} -
+ +
+ )} ); } diff --git a/front/lib/api/assistant/actions/database_query.ts b/front/lib/api/assistant/actions/database_query.ts index ca0bd9378c93..731fb14144fb 100644 --- a/front/lib/api/assistant/actions/database_query.ts +++ b/front/lib/api/assistant/actions/database_query.ts @@ -5,6 +5,7 @@ import { ConversationType, DatabaseQueryActionType, DatabaseQueryErrorEvent, + DatabaseQueryOutputEvent, DatabaseQueryParamsEvent, DatabaseQuerySuccessEvent, DustProdActionRegistry, @@ -119,7 +120,10 @@ export async function* runDatabaseQuery({ userMessage: UserMessageType; agentMessage: AgentMessageType; }): AsyncGenerator< - DatabaseQueryErrorEvent | DatabaseQuerySuccessEvent | DatabaseQueryParamsEvent + | DatabaseQueryErrorEvent + | DatabaseQuerySuccessEvent + | DatabaseQueryParamsEvent + | DatabaseQueryOutputEvent > { // Checking authorizations const owner = auth.workspace(); @@ -282,8 +286,36 @@ export async function* runDatabaseQuery({ return; } + if (event.content.block_name === "SQL") { + let tmpOutput = null; + if (e.value) { + const sql = e.value as string; + tmpOutput = { query: sql }; + } else { + tmpOutput = { no_query: true }; + } + yield { + type: "database_query_output", + created: Date.now(), + configurationId: configuration.sId, + messageId: agentMessage.sId, + action: { + id: action.id, + type: "database_query_action", + dataSourceWorkspaceId: action.dataSourceWorkspaceId, + dataSourceId: action.dataSourceId, + databaseId: action.databaseId, + params: action.params, + output: tmpOutput, + }, + }; + } + if (event.content.block_name === "OUTPUT" && e.value) { output = JSON.parse(e.value as string); + if (!output.query) { + output.no_query = true; + } } } } diff --git a/front/lib/api/assistant/agent.ts b/front/lib/api/assistant/agent.ts index 7644e918a85e..1b4aa1ee8598 100644 --- a/front/lib/api/assistant/agent.ts +++ b/front/lib/api/assistant/agent.ts @@ -302,6 +302,7 @@ export async function* runAgent( for await (const event of eventStream) { switch (event.type) { case "database_query_params": + case "database_query_output": yield event; break; case "database_query_error": diff --git a/front/lib/api/assistant/conversation.ts b/front/lib/api/assistant/conversation.ts index eb49b25443e5..bdfd875e0545 100644 --- a/front/lib/api/assistant/conversation.ts +++ b/front/lib/api/assistant/conversation.ts @@ -359,6 +359,7 @@ async function batchRenderAgentMessages( dataSourceWorkspaceId: action.dataSourceWorkspaceId, dataSourceId: action.dataSourceId, databaseId: action.databaseId, + params: action.params, output: action.output, }; }); @@ -1956,6 +1957,7 @@ async function* streamRunAgentEvents( case "dust_app_run_params": case "dust_app_run_block": case "database_query_params": + case "database_query_output": yield event; break; case "generation_tokens": diff --git a/front/lib/api/assistant/pubsub.ts b/front/lib/api/assistant/pubsub.ts index 63119c89c5d9..56f6470be4c1 100644 --- a/front/lib/api/assistant/pubsub.ts +++ b/front/lib/api/assistant/pubsub.ts @@ -158,6 +158,7 @@ async function handleUserMessageEvents( case "dust_app_run_params": case "dust_app_run_block": case "database_query_params": + case "database_query_output": case "agent_error": case "agent_action_success": case "generation_tokens": @@ -282,6 +283,7 @@ export async function retryAgentMessageWithPubSub( case "dust_app_run_params": case "dust_app_run_block": case "database_query_params": + case "database_query_output": case "agent_error": case "agent_action_success": case "generation_tokens": diff --git a/types/src/front/lib/api/assistant/actions/database_query.ts b/types/src/front/lib/api/assistant/actions/database_query.ts index de3033ef8b1a..3586f7f7a891 100644 --- a/types/src/front/lib/api/assistant/actions/database_query.ts +++ b/types/src/front/lib/api/assistant/actions/database_query.ts @@ -26,3 +26,11 @@ export type DatabaseQueryParamsEvent = { messageId: string; action: DatabaseQueryActionType; }; + +export type DatabaseQueryOutputEvent = { + type: "database_query_output"; + created: number; + configurationId: string; + messageId: string; + action: DatabaseQueryActionType; +}; diff --git a/types/src/front/lib/api/assistant/agent.ts b/types/src/front/lib/api/assistant/agent.ts index 7660c80dd644..93528860765b 100644 --- a/types/src/front/lib/api/assistant/agent.ts +++ b/types/src/front/lib/api/assistant/agent.ts @@ -6,7 +6,10 @@ import { AgentActionType, AgentMessageType, } from "../../../../front/assistant/conversation"; -import { DatabaseQueryParamsEvent } from "../../../../front/lib/api/assistant/actions/database_query"; +import { + DatabaseQueryOutputEvent, + DatabaseQueryParamsEvent, +} from "../../../../front/lib/api/assistant/actions/database_query"; import { DustAppRunBlockEvent, DustAppRunParamsEvent, @@ -41,7 +44,8 @@ export type AgentActionEvent = | RetrievalParamsEvent | DustAppRunParamsEvent | DustAppRunBlockEvent - | DatabaseQueryParamsEvent; + | DatabaseQueryParamsEvent + | DatabaseQueryOutputEvent; // Event sent once the action is completed, we're moving to generating a message if applicable. export type AgentActionSuccessEvent = { From b26fd218241e2df5c5de4416fcb658b5de3581c5 Mon Sep 17 00:00:00 2001 From: Stanislas Polu Date: Wed, 20 Dec 2023 16:16:38 +0100 Subject: [PATCH 07/23] x/evals: better prompting (#2975) --- x/spolu/research/evals/lib/algorithms/CoT.ts | 31 +++----------- x/spolu/research/evals/lib/datasets.ts | 8 ++-- x/spolu/research/evals/lib/datasets/MATH.ts | 40 ++++++++++++------- x/spolu/research/evals/lib/datasets/game24.ts | 38 ++++++++++-------- x/spolu/research/evals/lib/models/openai.ts | 2 + 5 files changed, 59 insertions(+), 60 deletions(-) diff --git a/x/spolu/research/evals/lib/algorithms/CoT.ts b/x/spolu/research/evals/lib/algorithms/CoT.ts index 2ce4de7ced69..6568f11b4078 100644 --- a/x/spolu/research/evals/lib/algorithms/CoT.ts +++ b/x/spolu/research/evals/lib/algorithms/CoT.ts @@ -39,10 +39,8 @@ export class CoT extends Algorithm { let prompt = `INSTRUCTIONS:\n`; prompt += ` ${this.dataset.instructions()}`; prompt += "\n\n"; - prompt += `Start by providing a REASONING consisting in multiple steps, using one line per step.`; + prompt += `Provide a reasoning consisting in multiple steps, using one line per step.`; prompt += ` ${this.dataset.reasoningStepInstructions()}`; - prompt += ` Finally provide a final ANSWER.`; - prompt += ` ${this.dataset.answerInstructions()}`; // prompt += // ` Do not perform multiple reasoning attempts per question,` + // ` do not backtrack in your reasoning steps.`; @@ -52,7 +50,6 @@ export class CoT extends Algorithm { for (const e of examples.slice(0, 4)) { prompt += `\nQUESTION: ${e.question}\n`; prompt += `REASONING:\n${e.reasoning.join("\n")}\n`; - prompt += `ANSWER: ${e.answer}\n`; } messages.push({ @@ -67,7 +64,7 @@ export class CoT extends Algorithm { }); messages.push({ role: "assistant", - content: `REASONING:\n${e.reasoning.join("\n")}\nANSWER: ${e.answer}`, + content: `REASONING:\n${e.reasoning.join("\n")}`, }); } @@ -79,18 +76,14 @@ export class CoT extends Algorithm { // console.log(prompt); // console.log(messages); - let maxTokens: number | undefined = undefined; - const datasetMaxTokens = this.dataset.maxTokens(); - if (datasetMaxTokens.reasoning && datasetMaxTokens.answer) { - maxTokens = datasetMaxTokens.reasoning + datasetMaxTokens.answer; - } - const query: ChatQuery = { provider: this.model.provider, model: this.model.model(), messages, temperature: this.TEMPERATURE, - maxTokens, + maxTokens: + this.dataset.maxTokens().reasoningStep * + this.dataset.maxTokens().maxStepCount, }; const c = await this.runCompletion(query); @@ -125,18 +118,7 @@ export class CoT extends Algorithm { console.log("+++++++++++++++++++++++++"); } - if (!c.content || !c.content.includes("REASONING:")) { - return await finish(test, c, query, false, ""); - } - - const content = c.content.split("REASONING:")[1].trim(); - - if (!content.includes("ANSWER:")) { - return await finish(test, c, query, false, ""); - } - - const reasoning = content.split("ANSWER:")[0].trim().split("\n"); - const answer = content.split("ANSWER:")[1].trim(); + const answer = this.dataset.parseAnswer(c.content); let check = false; try { @@ -146,7 +128,6 @@ export class CoT extends Algorithm { } if (debug) { - console.log(`REASONING: ${reasoning.join(" ")}`); console.log(`ANSWER: ${answer}`); console.log(`CHECK: ${check}`); console.log("-------------------------"); diff --git a/x/spolu/research/evals/lib/datasets.ts b/x/spolu/research/evals/lib/datasets.ts index 7f951f896542..aea27a852e43 100644 --- a/x/spolu/research/evals/lib/datasets.ts +++ b/x/spolu/research/evals/lib/datasets.ts @@ -24,14 +24,14 @@ export abstract class Dataset { abstract instructions(): string; abstract reasoningStepInstructions(): string; - abstract answerInstructions(): string; abstract maxTokens(): { - resaoningStep: number | null; - reasoning: number | null; - answer: number | null; + reasoningStep: number; + maxStepCount: number; }; + abstract parseAnswer(str: string): string; + abstract tests({ count }: { count: number }): Test[]; abstract examples({ diff --git a/x/spolu/research/evals/lib/datasets/MATH.ts b/x/spolu/research/evals/lib/datasets/MATH.ts index d9ba943bee9f..6d01c81d589f 100644 --- a/x/spolu/research/evals/lib/datasets/MATH.ts +++ b/x/spolu/research/evals/lib/datasets/MATH.ts @@ -36,6 +36,7 @@ export class MATH extends Dataset { d[e.type][e.level] = []; } d[e.type][e.level].push(e); + // console.log(e.reasoning.length); } return d; @@ -69,30 +70,39 @@ export class MATH extends Dataset { } instructions(): string { - return `Find a solution to the provided mathematical problem below.`; + return ( + "Find a solution to the provided mathematical problem." + + " The answer is a unique mathematical expression presented in LaTeX `\\boxed{}` directive. " + + " (example: `\\boxed{4}` or `\\boxed{3\\pi}`). Formatting instructions: " + + " fractions should be represented in the LaTeX form `\\frac{a}{b}` (not `\\frac12`)," + + " units should not be included," + + " square roots should be presented in the LaTeX form `\\sqrt{c}` (not `\\sqrt2`)," + + " all spaces and non critical parentheses or formatting should be stripped," + + " rational numbers should be presented with a leading `0`." + ); } reasoningStepInstructions(): string { - return `A reasoning step is one coherent step of mathematical reasoning it should held in one line.`; - } - - answerInstructions(): string { return ( - ` The answer is a unique mathematical expression presented in a LaTeX '\\boxed' directive` + - ` (eg: \\boxed{4} or \\boxed{3\\pi}). Formatting instructions:` + - ` fractions should be represented in the LaTeX form \\frac{a}{b} (not \\frac12),` + - ` units should not be included,` + - ` square roots should be presented in the LaTeX form \\sqrt{c} (not \\sqrt2),` + - ` all spaces and non critical parentheses or formatting should be stripped,` + - ` rational numbers should be presented with a leading 0.` + "A reasoning step is one coherent step of mathematical reasoning. It should hold in one line" + + " of at most 500 characters." + + " If an answer is reached as part of the reasoning, it should be included" + + " in the reasoning step using the `\\boxed{}` directive." ); } + parseAnswer(str: string): string { + const boxed = str.match(/\\boxed{([^}]*)}/g); + if (!boxed) { + return ""; + } + return boxed[boxed.length - 1]; + } + maxTokens() { return { - resaoningStep: 512, - reasoning: 3584, - answer: 64, + reasoningStep: 256, + maxStepCount: 16, }; } diff --git a/x/spolu/research/evals/lib/datasets/game24.ts b/x/spolu/research/evals/lib/datasets/game24.ts index 5371a34963b9..bc62fe07cf03 100644 --- a/x/spolu/research/evals/lib/datasets/game24.ts +++ b/x/spolu/research/evals/lib/datasets/game24.ts @@ -80,7 +80,7 @@ export class Game24 extends Dataset { if (result !== 24) { throw new Error("Unexpected non 24 result"); } - const r = `${a}${op}${b}=${result}`; + const r = `${a}${op}${b}=${result}, \\boxed{${solution}}`; reasoning.push(r); } } @@ -116,33 +116,39 @@ export class Game24 extends Dataset { instructions(): string { return ( - `Given a set of 4 input numbers, find a mathematical expression using each number` + - ` exactly once that symbolically evaluates to 24 (Game of 24).` + - ` The available operators are [+,-,*,/]` + - ` (the division operator / is the symbolic division (eg: 2/(3-5/2) = 2/(1/2) = 4)).` + "Given a set of 4 input numbers, find a mathematical expression using each number" + + " exactly once that symbolically evaluates to 24 (Game of 24)." + + " The available operators are [+,-,*,/]" + + " (the division operator / is the symbolic division (`2/(3-5/2) = 2/(1/2) = 4`))." ); } reasoningStepInstructions(): string { return ( - `A reasoning step is one operation involving 2 numbers followed by the numbers left to form` + - ` 24 after that operation (eg: '10*7=70, left: 70 2 11').` + - ` There is always exactly 3 reasoning steps per question.` + "A reasoning step is one operation involving 2 numbers followed by the numbers left to form" + + " 24 after that operation, separated by a comma (example: `10*7=70, left: 70 2 11`)." + + " There is always exactly 3 reasoning steps per question in Game of 24." + + " The last step should present the last operation and the solution expression" + + " using the `\\boxed{}` directive, sperated by a comma" + + " (example: `35-11=24, \\\boxed{(6+1)*5-11}`)." ); } - answerInstructions(): string { - return ( - `The answer should be a valid solution expression without space using each number` + - ` exactly once (eg: '(6+1)*5-11' or '(9-1)*9/3').` - ); + parseAnswer(str: string): string { + const boxed = str.match(/\\boxed{([^}]*)}/g); + if (!boxed) { + return ""; + } + // remove the \boxed{} directive + const answer = boxed.map((s) => s.slice(7, s.length - 1)); + // return the last one + return answer[answer.length - 1]; } maxTokens() { return { - resaoningStep: 32, - reasoning: 32 * 3, - answer: 16, + reasoningStep: 32, + maxStepCount: 3, }; } diff --git a/x/spolu/research/evals/lib/models/openai.ts b/x/spolu/research/evals/lib/models/openai.ts index f734bc058ef6..3bd4ef2b6fd3 100644 --- a/x/spolu/research/evals/lib/models/openai.ts +++ b/x/spolu/research/evals/lib/models/openai.ts @@ -28,8 +28,10 @@ export class OpenAIModel extends Model { messages: query.messages, max_tokens: query.maxTokens, temperature: query.temperature, + // logprobs: true, }); + // console.log(JSON.stringify(completion)); const m = completion.choices[0].message; if (m.content === null) { From 14b0598f4bdc7553d4f7281702961080164d240f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daphn=C3=A9=20Popin?= Date: Wed, 20 Dec 2023 16:46:10 +0100 Subject: [PATCH 08/23] Disable Gemini Pro from assistant builder (#2976) --- front/components/assistant_builder/AssistantBuilder.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/front/components/assistant_builder/AssistantBuilder.tsx b/front/components/assistant_builder/AssistantBuilder.tsx index 88e74e52333a..3237595cfcb2 100644 --- a/front/components/assistant_builder/AssistantBuilder.tsx +++ b/front/components/assistant_builder/AssistantBuilder.tsx @@ -24,7 +24,6 @@ import { UserType, WorkspaceType } from "@dust-tt/types"; import { CLAUDE_DEFAULT_MODEL_CONFIG, CLAUDE_INSTANT_DEFAULT_MODEL_CONFIG, - GEMINI_PRO_DEFAULT_MODEL_CONFIG, GPT_3_5_TURBO_MODEL_CONFIG, GPT_4_TURBO_MODEL_CONFIG, MISTRAL_SMALL_MODEL_CONFIG, @@ -81,7 +80,6 @@ const usedModelConfigs = [ CLAUDE_DEFAULT_MODEL_CONFIG, CLAUDE_INSTANT_DEFAULT_MODEL_CONFIG, MISTRAL_SMALL_MODEL_CONFIG, - GEMINI_PRO_DEFAULT_MODEL_CONFIG, ]; // Actions From 1dbca9deb727b7e3c33fd878eefd82629019ecea Mon Sep 17 00:00:00 2001 From: Flavien David Date: Wed, 20 Dec 2023 16:47:48 +0100 Subject: [PATCH 09/23] Support mistral-medium in Assistant Builder (#2977) --- front/components/assistant_builder/AssistantBuilder.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/front/components/assistant_builder/AssistantBuilder.tsx b/front/components/assistant_builder/AssistantBuilder.tsx index 3237595cfcb2..d53879d86aaa 100644 --- a/front/components/assistant_builder/AssistantBuilder.tsx +++ b/front/components/assistant_builder/AssistantBuilder.tsx @@ -26,6 +26,7 @@ import { CLAUDE_INSTANT_DEFAULT_MODEL_CONFIG, GPT_3_5_TURBO_MODEL_CONFIG, GPT_4_TURBO_MODEL_CONFIG, + MISTRAL_MEDIUM_MODEL_CONFIG, MISTRAL_SMALL_MODEL_CONFIG, SupportedModel, } from "@dust-tt/types"; @@ -79,6 +80,7 @@ const usedModelConfigs = [ GPT_3_5_TURBO_MODEL_CONFIG, CLAUDE_DEFAULT_MODEL_CONFIG, CLAUDE_INSTANT_DEFAULT_MODEL_CONFIG, + MISTRAL_MEDIUM_MODEL_CONFIG, MISTRAL_SMALL_MODEL_CONFIG, ]; From fd873102991ec282c67d2d8263b8776ef73efd0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daphn=C3=A9=20Popin?= Date: Wed, 20 Dec 2023 17:09:04 +0100 Subject: [PATCH 10/23] Front: Put database management in Folder section if feat activated (#2931) * Front: Put database management in Folder section if feat activated * Move to Full screen modal and rework form * Allow naming both db and table * Reset tab query param once used to set the valid tab * Rework field description --- front/lib/development.ts | 5 + front/lib/swr.ts | 1 + .../[name]/databases/[dId]/tables/index.ts | 7 +- .../data_sources/[name]/databases/index.ts | 7 +- .../[name]/databases/[dId]/tables/index.ts | 4 +- .../data_sources/[name]/databases/csv.ts | 16 +- .../data_sources/[name]/databases/index.ts | 4 +- .../data-sources/[name]/databases/upsert.tsx | 424 ++++++++++++++++++ .../builder/data-sources/[name]/index.tsx | 368 ++++++++++++--- .../builder/data-sources/[name]/upsert.tsx | 2 +- types/src/front/lib/actions/registry.ts | 4 +- types/src/front/lib/core_api.ts | 16 +- 12 files changed, 766 insertions(+), 92 deletions(-) create mode 100644 front/pages/w/[wId]/builder/data-sources/[name]/databases/upsert.tsx diff --git a/front/lib/development.ts b/front/lib/development.ts index b9ed08361b96..351c2b04b227 100644 --- a/front/lib/development.ts +++ b/front/lib/development.ts @@ -14,3 +14,8 @@ export function isDevelopmentOrDustWorkspace(owner: WorkspaceType) { owner.sId === PRODUCTION_DUST_APPS_WORKSPACE_ID ); } + +export function isActivatedStructuredDB(owner: WorkspaceType) { + // We will manually add workspace ids here. + return isDevelopmentOrDustWorkspace(owner); +} diff --git a/front/lib/swr.ts b/front/lib/swr.ts index 81678105b9c8..a81ba3316e6f 100644 --- a/front/lib/swr.ts +++ b/front/lib/swr.ts @@ -511,6 +511,7 @@ export function useDatabases({ return { databases: data ? data.databases : [], + total: data ? data.total : null, isDatabasesLoading: !error && !data, isDatabasesError: error, mutateDatabases: mutate, diff --git a/front/pages/api/v1/w/[wId]/data_sources/[name]/databases/[dId]/tables/index.ts b/front/pages/api/v1/w/[wId]/data_sources/[name]/databases/[dId]/tables/index.ts index c3ccc44facea..2fc0cb324ebb 100644 --- a/front/pages/api/v1/w/[wId]/data_sources/[name]/databases/[dId]/tables/index.ts +++ b/front/pages/api/v1/w/[wId]/data_sources/[name]/databases/[dId]/tables/index.ts @@ -1,4 +1,4 @@ -import { CoreAPI, CoreAPIDatabaseTable } from "@dust-tt/types"; +import { CoreAPI, CoreAPIDatabase, CoreAPIDatabaseTable } from "@dust-tt/types"; import { isLeft } from "fp-ts/lib/Either"; import * as t from "io-ts"; import * as reporter from "io-ts-reporters"; @@ -12,6 +12,7 @@ import logger from "@app/logger/logger"; import { apiError, withLogging } from "@app/logger/withlogging"; export type ListDatabaseTablesResponseBody = { + database: CoreAPIDatabase; tables: CoreAPIDatabaseTable[]; }; @@ -106,9 +107,9 @@ async function handler( }); } - const { tables } = tablesRes.value; + const { database, tables } = tablesRes.value; - return res.status(200).json({ tables }); + return res.status(200).json({ database, tables }); case "POST": const bodyValidation = UpsertDatabaseTableRequestBodySchema.decode( diff --git a/front/pages/api/v1/w/[wId]/data_sources/[name]/databases/index.ts b/front/pages/api/v1/w/[wId]/data_sources/[name]/databases/index.ts index ca5d4862ec73..d4fe4bbce1ff 100644 --- a/front/pages/api/v1/w/[wId]/data_sources/[name]/databases/index.ts +++ b/front/pages/api/v1/w/[wId]/data_sources/[name]/databases/index.ts @@ -25,6 +25,9 @@ export const ListDatabasesReqQuerySchema = t.type({ }); export type ListDatabasesResponseBody = { databases: CoreAPIDatabase[]; + offset: number; + limit: number; + total: number; }; async function handler( @@ -157,9 +160,7 @@ async function handler( }); } - const { databases } = getRes.value; - - return res.status(200).json({ databases }); + return res.status(200).json(getRes.value); default: return apiError(req, res, { diff --git a/front/pages/api/w/[wId]/data_sources/[name]/databases/[dId]/tables/index.ts b/front/pages/api/w/[wId]/data_sources/[name]/databases/[dId]/tables/index.ts index 531aa4a46bc9..84f950f343bb 100644 --- a/front/pages/api/w/[wId]/data_sources/[name]/databases/[dId]/tables/index.ts +++ b/front/pages/api/w/[wId]/data_sources/[name]/databases/[dId]/tables/index.ts @@ -103,9 +103,9 @@ async function handler( }); } - const { tables } = tablesRes.value; + const { database, tables } = tablesRes.value; - return res.status(200).json({ tables }); + return res.status(200).json({ database, tables }); default: return apiError(req, res, { diff --git a/front/pages/api/w/[wId]/data_sources/[name]/databases/csv.ts b/front/pages/api/w/[wId]/data_sources/[name]/databases/csv.ts index bd7f2b0237ec..55e3f438fe17 100644 --- a/front/pages/api/w/[wId]/data_sources/[name]/databases/csv.ts +++ b/front/pages/api/w/[wId]/data_sources/[name]/databases/csv.ts @@ -15,8 +15,9 @@ import logger from "@app/logger/logger"; import { apiError, withLogging } from "@app/logger/withlogging"; const CreateDatabaseFromCsvSchema = t.type({ - name: t.string, - description: t.string, + databaseName: t.string, + tableName: t.string, + tableDescription: t.string, csv: t.string, }); @@ -79,7 +80,8 @@ async function handler( }); } - const { name, description, csv } = bodyValidation.right; + const { databaseName, tableName, tableDescription, csv } = + bodyValidation.right; const csvRowsRes = await rowsFromCsv(csv); if (csvRowsRes.isErr()) { return apiError(req, res, { @@ -105,14 +107,14 @@ async function handler( projectId: dataSource.dustAPIProjectId, dataSourceName: dataSource.name, databaseId: id, - name, + name: databaseName, }); if (dbRes.isErr()) { logger.error( { dataSourceName: dataSource.name, workspaceId: owner.id, - databaseName: name, + databaseName: databaseName, databaseId: id, error: dbRes.error, }, @@ -134,8 +136,8 @@ async function handler( projectId: dataSource.dustAPIProjectId, dataSourceName: dataSource.name, databaseId: id, - description, - name: name, + description: tableDescription, + name: tableName, tableId, }); diff --git a/front/pages/api/w/[wId]/data_sources/[name]/databases/index.ts b/front/pages/api/w/[wId]/data_sources/[name]/databases/index.ts index 804485c058e2..da786ce08f48 100644 --- a/front/pages/api/w/[wId]/data_sources/[name]/databases/index.ts +++ b/front/pages/api/w/[wId]/data_sources/[name]/databases/index.ts @@ -112,9 +112,7 @@ async function handler( }); } - const { databases } = getRes.value; - - return res.status(200).json({ databases }); + return res.status(200).json(getRes.value); default: return apiError(req, res, { diff --git a/front/pages/w/[wId]/builder/data-sources/[name]/databases/upsert.tsx b/front/pages/w/[wId]/builder/data-sources/[name]/databases/upsert.tsx new file mode 100644 index 000000000000..4d40f587ae45 --- /dev/null +++ b/front/pages/w/[wId]/builder/data-sources/[name]/databases/upsert.tsx @@ -0,0 +1,424 @@ +import { + Button, + DocumentPlusIcon, + DropdownMenu, + Input, + Page, + TrashIcon, +} from "@dust-tt/sparkle"; +import { DataSourceType, UserType, WorkspaceType } from "@dust-tt/types"; +import { SubscriptionType } from "@dust-tt/types"; +import { GetServerSideProps, InferGetServerSidePropsType } from "next"; +import { useRouter } from "next/router"; +import { useContext, useEffect, useRef, useState } from "react"; +import { mutate } from "swr"; + +import AppLayout from "@app/components/sparkle/AppLayout"; +import { AppLayoutSimpleSaveCancelTitle } from "@app/components/sparkle/AppLayoutTitle"; +import { subNavigationAssistants } from "@app/components/sparkle/navigation"; +import { SendNotificationsContext } from "@app/components/sparkle/Notification"; +import { getDataSource } from "@app/lib/api/data_sources"; +import { Authenticator, getSession, getUserFromSession } from "@app/lib/auth"; +import { handleFileUploadToText } from "@app/lib/client/handle_file_upload"; +import { classNames } from "@app/lib/utils"; + +const { GA_TRACKING_ID = "" } = process.env; + +export const getServerSideProps: GetServerSideProps<{ + user: UserType | null; + owner: WorkspaceType; + subscription: SubscriptionType; + readOnly: boolean; + dataSource: DataSourceType; + loadDatabaseId: string | null; + gaTrackingId: string; +}> = async (context) => { + const session = await getSession(context.req, context.res); + const user = await getUserFromSession(session); + const auth = await Authenticator.fromSession( + session, + context.params?.wId as string + ); + + const owner = auth.workspace(); + const plan = auth.plan(); + const subscription = auth.subscription(); + if (!owner || !plan || !subscription) { + return { + notFound: true, + }; + } + + const dataSource = await getDataSource(auth, context.params?.name as string); + if (!dataSource) { + return { + notFound: true, + }; + } + + // If user is not builder or if datasource is managed. + const readOnly = !auth.isBuilder() || !!dataSource.connectorId; + + return { + props: { + user, + owner, + subscription, + readOnly, + dataSource, + loadDatabaseId: (context.query.databaseId || null) as string | null, + gaTrackingId: GA_TRACKING_ID, + }, + }; +}; + +export default function DatabaseUpsert({ + user, + owner, + subscription, + readOnly, + dataSource, + loadDatabaseId, + gaTrackingId, +}: InferGetServerSidePropsType) { + const router = useRouter(); + const sendNotification = useContext(SendNotificationsContext); + const fileInputRef = useRef(null); + + const [databaseId, setDatabaseId] = useState(null); + const [databaseName, setDatabaseName] = useState(""); + const [tableName, setTableName] = useState(""); + const [tableDescription, setTableDescription] = useState(""); + const [file, setFile] = useState(null); + + const [disabled, setDisabled] = useState(false); + const [loading, setLoading] = useState(false); + const [uploading, setUploading] = useState(false); + + useEffect(() => { + setDisabled(!databaseName || !tableName || !tableDescription || !file); + }, [databaseName, tableName, tableDescription, file]); + + useEffect(() => { + if (loadDatabaseId) { + setDatabaseName(loadDatabaseId); + setDisabled(true); + fetch( + `/api/w/${owner.sId}/data_sources/${ + dataSource.name + }/databases/${encodeURIComponent(loadDatabaseId)}/tables` + ) + .then(async (res) => { + if (res.ok) { + const { database, tables } = await res.json(); + const table = tables[0]; // TODO: support multiple tables + setDisabled(false); + setDatabaseId(database.database_id); + setDatabaseName(database.name); + setTableName(table.name); + setTableDescription(table.description); + } + }) + .catch((e) => console.error(e)); + } + }, [dataSource.name, loadDatabaseId, owner.sId]); + + // Not empty, only alphanumeric, and not too long + const isNameValid = (name: string) => + name !== "" && /^[a-zA-Z0-9_]{1,32}$/.test(name); + + const redirectToDataSourcePage = () => { + void router.push( + `/w/${owner.sId}/builder/data-sources/${dataSource.name}?tab=databases` + ); + }; + + const handleDelete = async () => { + const res = await fetch( + `/api/w/${owner.sId}/data_sources/${dataSource.name}/databases/${databaseId}`, + { + method: "DELETE", + } + ); + + if (!res.ok) { + sendNotification({ + type: "error", + title: "Error deleting database", + description: `An error occured: ${await res.text()}.`, + }); + return; + } + await mutate( + `/api/w/${owner.sId}/data_sources/${dataSource.name}/databases?offset=0&limit=100` + ); + redirectToDataSourcePage(); + }; + + const handleUpsert = async () => { + if (!file) { + return; + } + + const res = await handleFileUploadToText(file); + if (res.isErr()) { + sendNotification({ + type: "error", + title: "Error uploading file", + description: `An unexpected error occured: ${res.error}.`, + }); + return; + } + const { content } = res.value; + if (res.value.content.length > 1000000) { + sendNotification({ + type: "error", + title: "File too large", + description: + "Please upload a file containing less than 1 million characters.", + }); + return; + } + + const uploadRes = await fetch( + `/api/w/${owner.sId}/data_sources/${dataSource.name}/databases/csv`, + { + method: "POST", + body: JSON.stringify({ + databaseName, + tableName, + tableDescription, + csv: content, + }), + headers: { + "Content-Type": "application/json", + }, + } + ); + + if (!uploadRes.ok) { + sendNotification({ + type: "error", + title: "Error uploading file", + description: `An error occured: ${await uploadRes.text()}.`, + }); + return; + } + + await mutate( + `/api/w/${owner.sId}/data_sources/${dataSource.name}/databases?offset=0&limit=100` + ); + redirectToDataSourcePage(); + }; + + return ( + { + await handleUpsert(); + } + : undefined + } + isSaving={loading} + /> + } + hideSidebar={true} + > +
+ +
+ +
+ setDatabaseName(v)} + error={ + !databaseName || isNameValid(databaseName) + ? null + : "Invalid name: Must be alphanumeric, max 32 characters and no space." + } + showErrorLabel={true} + /> +
+
+ +
+ +
+ setTableName(v)} + error={ + !tableName || isNameValid(tableName) + ? null + : "Invalid name: Must be alphanumeric, max 32 characters and no space." + } + showErrorLabel={true} + /> +
+
+ +
+ +
+