-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
16 changed files
with
491 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
--- | ||
title: Mistral AI | ||
--- | ||
|
||
Similarly to the [OpenAI block](./openai), this block allows you to chat with Mistral's AI models. | ||
|
||
Create your Mistral account here: https://console.mistral.ai. | ||
|
||
## Create chat completion | ||
|
||
With the Mistral AI block, you can create a chat completion based on your user queries and display the answer back to your typebot. | ||
|
||
This action comes with a convenient message type called `Dialogue`. It allows you to easily pass a sequence of saved assistant / user messages history to Mistral AI | ||
|
||
## Troobleshooting | ||
|
||
- If you get HTTP 401 error while loading the Mistral models, it means your API key is still not propagated on Mistral's side. Please wait a few minutes and try again. | ||
|
||
- If you get a rate limit error, make sure to add a subscription to your Mistral account. You can do so by going to your [Mistral billing section](https://console.mistral.ai/billing/) and clicking on the `Subscribe` button. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26852,7 +26852,8 @@ | |
"zemantic-ai", | ||
"cal-com", | ||
"chat-node", | ||
"qr-code" | ||
"qr-code", | ||
"mistral" | ||
] | ||
}, | ||
"options": {} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10353,7 +10353,8 @@ | |
"zemantic-ai", | ||
"cal-com", | ||
"chat-node", | ||
"qr-code" | ||
"qr-code", | ||
"mistral" | ||
] | ||
}, | ||
"options": {} | ||
|
125 changes: 125 additions & 0 deletions
125
packages/forge/blocks/mistral/actions/createChatCompletion.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import { option, createAction } from '@typebot.io/forge' | ||
import { isDefined } from '@typebot.io/lib' | ||
import { auth } from '../auth' | ||
import MistralClient from '@mistralai/mistralai' | ||
import { parseMessages } from '../helpers/parseMessages' | ||
import { OpenAIStream } from 'ai' | ||
|
||
const nativeMessageContentSchema = { | ||
content: option.string.layout({ | ||
inputType: 'textarea', | ||
placeholder: 'Content', | ||
}), | ||
} | ||
|
||
const systemMessageItemSchema = option | ||
.object({ | ||
role: option.literal('system'), | ||
}) | ||
.extend(nativeMessageContentSchema) | ||
|
||
const userMessageItemSchema = option | ||
.object({ | ||
role: option.literal('user'), | ||
}) | ||
.extend(nativeMessageContentSchema) | ||
|
||
const assistantMessageItemSchema = option | ||
.object({ | ||
role: option.literal('assistant'), | ||
}) | ||
.extend(nativeMessageContentSchema) | ||
|
||
const dialogueMessageItemSchema = option.object({ | ||
role: option.literal('Dialogue'), | ||
dialogueVariableId: option.string.layout({ | ||
inputType: 'variableDropdown', | ||
placeholder: 'Dialogue variable', | ||
}), | ||
startsBy: option.enum(['user', 'assistant']).layout({ | ||
label: 'starts by', | ||
direction: 'row', | ||
defaultValue: 'user', | ||
}), | ||
}) | ||
|
||
export const options = option.object({ | ||
model: option.string.layout({ | ||
placeholder: 'Select a model', | ||
fetcher: 'fetchModels', | ||
}), | ||
messages: option | ||
.array( | ||
option.discriminatedUnion('role', [ | ||
systemMessageItemSchema, | ||
userMessageItemSchema, | ||
assistantMessageItemSchema, | ||
dialogueMessageItemSchema, | ||
]) | ||
) | ||
.layout({ accordion: 'Messages', itemLabel: 'message', isOrdered: true }), | ||
responseMapping: option.saveResponseArray(['Message content']).layout({ | ||
accordion: 'Save response', | ||
}), | ||
}) | ||
|
||
export const createChatCompletion = createAction({ | ||
name: 'Create chat completion', | ||
auth, | ||
options, | ||
getSetVariableIds: (options) => | ||
options.responseMapping?.map((res) => res.variableId).filter(isDefined) ?? | ||
[], | ||
fetchers: [ | ||
{ | ||
id: 'fetchModels', | ||
dependencies: [], | ||
fetch: async ({ credentials }) => { | ||
const client = new MistralClient(credentials.apiKey) | ||
|
||
const listModelsResponse = await client.listModels() | ||
|
||
return ( | ||
listModelsResponse.data | ||
.sort((a, b) => b.created - a.created) | ||
.map((model) => model.id) ?? [] | ||
) | ||
}, | ||
}, | ||
], | ||
run: { | ||
server: async ({ credentials: { apiKey }, options, variables, logs }) => { | ||
if (!options.model) return logs.add('No model selected') | ||
const client = new MistralClient(apiKey) | ||
|
||
const response = await client.chat({ | ||
model: options.model, | ||
messages: parseMessages({ options, variables }), | ||
}) | ||
|
||
options.responseMapping?.forEach((mapping) => { | ||
if (!mapping.variableId) return | ||
if (!mapping.item || mapping.item === 'Message content') | ||
variables.set(mapping.variableId, response.choices[0].message.content) | ||
}) | ||
}, | ||
stream: { | ||
getStreamVariableId: (options) => | ||
options.responseMapping?.find( | ||
(res) => res.item === 'Message content' || !res.item | ||
)?.variableId, | ||
run: async ({ credentials: { apiKey }, options, variables }) => { | ||
if (!options.model) return | ||
const client = new MistralClient(apiKey) | ||
|
||
const response = client.chatStream({ | ||
model: options.model, | ||
messages: parseMessages({ options, variables }), | ||
}) | ||
|
||
// @ts-ignore https://github.com/vercel/ai/issues/936 | ||
return OpenAIStream(response) | ||
}, | ||
}, | ||
}, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { option, AuthDefinition } from '@typebot.io/forge' | ||
|
||
export const auth = { | ||
type: 'encryptedCredentials', | ||
name: 'Mistral account', | ||
schema: option.object({ | ||
apiKey: option.string.layout({ | ||
label: 'API key', | ||
isRequired: true, | ||
inputType: 'password', | ||
helperText: | ||
'You can generate an API key [here](https://console.mistral.ai/user/api-keys/).', | ||
}), | ||
}), | ||
} satisfies AuthDefinition |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { options as createChatCompletionOption } from '../actions/createChatCompletion' | ||
import { ReadOnlyVariableStore } from '@typebot.io/forge' | ||
import { isDefined, isNotEmpty } from '@typebot.io/lib' | ||
import { z } from '@typebot.io/forge/zod' | ||
|
||
export const parseMessages = ({ | ||
options: { messages }, | ||
variables, | ||
}: { | ||
options: Pick<z.infer<typeof createChatCompletionOption>, 'messages'> | ||
variables: ReadOnlyVariableStore | ||
}) => | ||
messages | ||
?.flatMap((message) => { | ||
if (!message.role) return | ||
|
||
if (message.role === 'Dialogue') { | ||
if (!message.dialogueVariableId) return | ||
const dialogue = variables.get(message.dialogueVariableId) ?? [] | ||
const dialogueArr = Array.isArray(dialogue) ? dialogue : [dialogue] | ||
|
||
return dialogueArr.map((dialogueItem, index) => { | ||
if (dialogueItem === null) return | ||
if (index === 0 && message.startsBy === 'assistant') | ||
return { | ||
role: 'assistant', | ||
content: dialogueItem, | ||
} | ||
return { | ||
role: | ||
index % (message.startsBy === 'assistant' ? 1 : 2) === 0 | ||
? 'user' | ||
: 'assistant', | ||
content: dialogueItem, | ||
} | ||
}) | ||
} | ||
|
||
if (!message.content) return | ||
|
||
return { | ||
role: message.role, | ||
content: variables.parse(message.content), | ||
} | ||
}) | ||
.filter(isDefined) | ||
.filter( | ||
(message) => | ||
isNotEmpty(message?.role) && isNotEmpty(message?.content?.toString()) | ||
) ?? [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { createBlock } from '@typebot.io/forge' | ||
import { MistralLogo } from './logo' | ||
import { auth } from './auth' | ||
import { createChatCompletion } from './actions/createChatCompletion' | ||
|
||
export const mistral = createBlock({ | ||
id: 'mistral', | ||
name: 'Mistral', | ||
tags: ['ai', 'chat', 'completion'], | ||
LightLogo: MistralLogo, | ||
auth, | ||
actions: [createChatCompletion], | ||
docsUrl: 'https://docs.typebot.io/forge/blocks/mistral', | ||
}) |
Oops, something went wrong.