mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-29 11:01:18 +03:00
LS Prompt Hub: Moving calls to server side and adding functionality to show the detailed prompt
This commit is contained in:
@@ -64,6 +64,9 @@ import { ChatflowPool } from './ChatflowPool'
|
|||||||
import { CachePool } from './CachePool'
|
import { CachePool } from './CachePool'
|
||||||
import { ICommonObject, INodeOptionsValue } from 'flowise-components'
|
import { ICommonObject, INodeOptionsValue } from 'flowise-components'
|
||||||
import { createRateLimiter, getRateLimiter, initializeRateLimiter } from './utils/rateLimit'
|
import { createRateLimiter, getRateLimiter, initializeRateLimiter } from './utils/rateLimit'
|
||||||
|
import axios from 'axios'
|
||||||
|
import { Client } from 'langchainhub'
|
||||||
|
import { parsePrompt } from './utils/hub'
|
||||||
|
|
||||||
export class App {
|
export class App {
|
||||||
app: express.Application
|
app: express.Application
|
||||||
@@ -1093,6 +1096,43 @@ export class App {
|
|||||||
await this.buildChatflow(req, res, undefined, true, true)
|
await this.buildChatflow(req, res, undefined, true, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Prompt from Hub
|
||||||
|
// ----------------------------------------
|
||||||
|
this.app.post('/api/v1/load-prompt', async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const credential = await this.AppDataSource.getRepository(Credential).findOneBy({
|
||||||
|
id: req.body.credential
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!credential) return res.status(404).json({ error: `Credential ${req.body.credential} not found` })
|
||||||
|
|
||||||
|
// Decrypt credentialData
|
||||||
|
const decryptedCredentialData = await decryptCredentialData(credential.encryptedData, credential.credentialName, undefined)
|
||||||
|
let hub = new Client({ apiKey: decryptedCredentialData.langsmithApiKey, apiUrl: decryptedCredentialData.langsmithEndpoint })
|
||||||
|
const prompt = await hub.pull(req.body.promptName)
|
||||||
|
const templates = parsePrompt(prompt)
|
||||||
|
|
||||||
|
return res.json({ status: 'OK', prompt: req.body.promptName, templates: templates })
|
||||||
|
} catch (e: any) {
|
||||||
|
return res.json({ status: 'ERROR', prompt: req.body.promptName, error: e?.message })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.app.post('/api/v1/prompts-list', async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const tags = req.body.tags ? `tags=${req.body.tags}` : ''
|
||||||
|
const url = `https://web.hub.langchain.com/repos/?${tags}offset=0&limit=20&has_commits=true&sort_field=num_likes&sort_direction=desc&is_archived=false`
|
||||||
|
axios.get(url).then((response) => {
|
||||||
|
if (response.data.repos) {
|
||||||
|
return res.json({ status: 'OK', repos: response.data.repos })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (e: any) {
|
||||||
|
return res.json({ status: 'ERROR', repos: [] })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// Prediction
|
// Prediction
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
export function parsePrompt(prompt: string): any[] {
|
||||||
|
const promptObj = JSON.parse(prompt)
|
||||||
|
let response = []
|
||||||
|
if (promptObj.kwargs.messages) {
|
||||||
|
promptObj.kwargs.messages.forEach((message: any) => {
|
||||||
|
let messageType = message.id.includes('SystemMessagePromptTemplate')
|
||||||
|
? 'systemMessagePrompt'
|
||||||
|
: message.id.includes('HumanMessagePromptTemplate')
|
||||||
|
? 'humanMessagePrompt'
|
||||||
|
: message.id.includes('AIMessagePromptTemplate')
|
||||||
|
? 'aiMessagePrompt'
|
||||||
|
: 'template'
|
||||||
|
let template = message.kwargs.prompt.kwargs.template
|
||||||
|
response.push({
|
||||||
|
type: messageType,
|
||||||
|
template: template
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else if (promptObj.kwargs.template) {
|
||||||
|
let template = promptObj.kwargs.template
|
||||||
|
response.push({
|
||||||
|
type: 'template',
|
||||||
|
template: template
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import client from './client'
|
||||||
|
|
||||||
|
const getAvailablePrompts = (body) => client.post(`/prompts-list`, body)
|
||||||
|
const getPrompt = (body) => client.post(`/load-prompt`, body)
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getAvailablePrompts,
|
||||||
|
getPrompt
|
||||||
|
}
|
||||||
@@ -25,9 +25,9 @@ import { useDispatch } from 'react-redux'
|
|||||||
import FormControl from '@mui/material/FormControl'
|
import FormControl from '@mui/material/FormControl'
|
||||||
import Checkbox from '@mui/material/Checkbox'
|
import Checkbox from '@mui/material/Checkbox'
|
||||||
import MenuItem from '@mui/material/MenuItem'
|
import MenuItem from '@mui/material/MenuItem'
|
||||||
import axios from 'axios'
|
|
||||||
import ReactMarkdown from 'react-markdown'
|
import ReactMarkdown from 'react-markdown'
|
||||||
import CredentialInputHandler from '../../views/canvas/CredentialInputHandler'
|
import CredentialInputHandler from '../../views/canvas/CredentialInputHandler'
|
||||||
|
import promptApi from '../../api/prompt'
|
||||||
|
|
||||||
const PromptLangsmithHubDialog = ({ promptType, show, onCancel }) => {
|
const PromptLangsmithHubDialog = ({ promptType, show, onCancel }) => {
|
||||||
const portalElement = document.getElementById('portal')
|
const portalElement = document.getElementById('portal')
|
||||||
@@ -96,13 +96,24 @@ const PromptLangsmithHubDialog = ({ promptType, show, onCancel }) => {
|
|||||||
{ id: 306, name: 'Spanish' }
|
{ id: 306, name: 'Spanish' }
|
||||||
]
|
]
|
||||||
const [language, setLanguage] = useState([])
|
const [language, setLanguage] = useState([])
|
||||||
const [prompts, setPrompts] = useState([])
|
const [availablePrompNameList, setAvailablePrompNameList] = useState([])
|
||||||
const [selectedPrompt, setSelectedPrompt] = useState({})
|
const [selectedPrompt, setSelectedPrompt] = useState({})
|
||||||
|
|
||||||
const [credentialId, setCredentialId] = useState('')
|
const [credentialId, setCredentialId] = useState('')
|
||||||
|
|
||||||
const handleListItemClick = (event, index) => {
|
const handleListItemClick = async (event, index) => {
|
||||||
setSelectedPrompt(prompts[index])
|
const prompt = availablePrompNameList[index]
|
||||||
|
|
||||||
|
if (!prompt.detailed) {
|
||||||
|
const createResp = await promptApi.getPrompt({
|
||||||
|
credential: credentialId,
|
||||||
|
promptName: selectedPrompt.full_name
|
||||||
|
})
|
||||||
|
if (createResp.data) {
|
||||||
|
prompt.detailed = createResp.data.templates
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setSelectedPrompt(prompt)
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchPrompts = async () => {
|
const fetchPrompts = async () => {
|
||||||
@@ -116,17 +127,15 @@ const PromptLangsmithHubDialog = ({ promptType, show, onCancel }) => {
|
|||||||
language.forEach((item) => {
|
language.forEach((item) => {
|
||||||
tags += `tags=${item.name}&`
|
tags += `tags=${item.name}&`
|
||||||
})
|
})
|
||||||
const url = `https://web.hub.langchain.com/repos/?${tags}offset=0&limit=20&has_commits=true&sort_field=num_likes&sort_direction=desc&is_archived=false`
|
const createResp = await promptApi.getAvailablePrompts({
|
||||||
axios.get(url).then((response) => {
|
credential: credentialId,
|
||||||
if (response.data.repos) {
|
tags: tags
|
||||||
setPrompts(response.data.repos)
|
|
||||||
// response.data.repos.forEach((item) => {
|
|
||||||
// console.log(item)
|
|
||||||
// })
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
// latestReleaseReq.then()
|
if (createResp.data) {
|
||||||
|
setAvailablePrompNameList(createResp.data.repos)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeDuplicates = (value) => {
|
const removeDuplicates = (value) => {
|
||||||
let duplicateRemoved = []
|
let duplicateRemoved = []
|
||||||
|
|
||||||
@@ -173,26 +182,29 @@ const PromptLangsmithHubDialog = ({ promptType, show, onCancel }) => {
|
|||||||
<DialogTitle sx={{ fontSize: '1rem' }} id='prompt-dialog-title'>
|
<DialogTitle sx={{ fontSize: '1rem' }} id='prompt-dialog-title'>
|
||||||
Load Prompts from Langsmith Hub ({promptType === 'template' ? 'PromptTemplate' : 'ChatPromptTemplate'})
|
Load Prompts from Langsmith Hub ({promptType === 'template' ? 'PromptTemplate' : 'ChatPromptTemplate'})
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent dividers>
|
<DialogContent dividers sx={{ p: 1 }}>
|
||||||
<Box sx={{ width: '100%' }} style={{ display: 'flex', flexDirection: 'row' }}>
|
<Box sx={{ width: '100%' }} style={{ display: 'flex', flexDirection: 'row', p: 1, m: 0 }}>
|
||||||
<Typography style={{ alignSelf: 'center' }} sx={{ mr: 5 }} variant='overline'>
|
<Typography style={{ alignSelf: 'center' }} sx={{ mr: 5 }} variant='overline'>
|
||||||
Langsmith Credential
|
Langsmith Credential
|
||||||
<span style={{ color: 'red' }}>*</span>
|
<span style={{ color: 'red' }}>*</span>
|
||||||
</Typography>
|
</Typography>
|
||||||
<CredentialInputHandler
|
<FormControl style={{ width: '30%' }} xs={4} sx={{ m: 1 }}>
|
||||||
sx={{ flexGrow: 1 }}
|
<CredentialInputHandler
|
||||||
key={credentialId}
|
size='small'
|
||||||
data={credentialId ? { credential: credentialId } : {}}
|
sx={{ flexGrow: 1 }}
|
||||||
inputParam={{
|
key={credentialId}
|
||||||
label: 'Connect Credential',
|
data={credentialId ? { credential: credentialId } : {}}
|
||||||
name: 'credential',
|
inputParam={{
|
||||||
type: 'credential',
|
label: 'Connect Credential',
|
||||||
credentialNames: ['openAIApi']
|
name: 'credential',
|
||||||
}}
|
type: 'credential',
|
||||||
onSelect={(newValue) => {
|
credentialNames: ['langsmithApi']
|
||||||
setCredentialId(newValue)
|
}}
|
||||||
}}
|
onSelect={(newValue) => {
|
||||||
/>
|
setCredentialId(newValue)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ width: '100%' }} style={{ display: 'flex', flexDirection: 'row' }}>
|
<Box sx={{ width: '100%' }} style={{ display: 'flex', flexDirection: 'row' }}>
|
||||||
<FormControl
|
<FormControl
|
||||||
@@ -206,6 +218,7 @@ const PromptLangsmithHubDialog = ({ promptType, show, onCancel }) => {
|
|||||||
Model
|
Model
|
||||||
</InputLabel>
|
</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
|
disabled={!credentialId}
|
||||||
id='model-checkbox'
|
id='model-checkbox'
|
||||||
labelId='model-checkbox-label'
|
labelId='model-checkbox-label'
|
||||||
multiple
|
multiple
|
||||||
@@ -236,6 +249,7 @@ const PromptLangsmithHubDialog = ({ promptType, show, onCancel }) => {
|
|||||||
</InputLabel>
|
</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
autoWidth={false}
|
autoWidth={false}
|
||||||
|
disabled={!credentialId}
|
||||||
labelId='usecase-checkbox-label'
|
labelId='usecase-checkbox-label'
|
||||||
id='usecase-checkbox'
|
id='usecase-checkbox'
|
||||||
multiple
|
multiple
|
||||||
@@ -267,6 +281,7 @@ const PromptLangsmithHubDialog = ({ promptType, show, onCancel }) => {
|
|||||||
<Select
|
<Select
|
||||||
labelId='language-checkbox-label'
|
labelId='language-checkbox-label'
|
||||||
id='language-checkbox'
|
id='language-checkbox'
|
||||||
|
disabled={!credentialId}
|
||||||
multiple
|
multiple
|
||||||
size='small'
|
size='small'
|
||||||
value={language}
|
value={language}
|
||||||
@@ -299,13 +314,13 @@ const PromptLangsmithHubDialog = ({ promptType, show, onCancel }) => {
|
|||||||
<Grid xs={12} container spacing={2} justifyContent='center' alignItems='center'>
|
<Grid xs={12} container spacing={2} justifyContent='center' alignItems='center'>
|
||||||
<Grid xs={4} item style={{ textAlign: 'left' }}>
|
<Grid xs={4} item style={{ textAlign: 'left' }}>
|
||||||
<Box sx={{ width: '100%', maxWidth: 360 }}>
|
<Box sx={{ width: '100%', maxWidth: 360 }}>
|
||||||
<Card variant='outlined' style={{ height: 430, overflow: 'auto' }}>
|
<Card variant='outlined' style={{ height: 470, overflow: 'auto' }}>
|
||||||
<CardContent sx={{ p: 1 }}>
|
<CardContent sx={{ p: 1 }}>
|
||||||
<Typography sx={{ fontSize: 10 }} color='text.secondary' gutterBottom>
|
<Typography sx={{ fontSize: 10 }} color='text.secondary' gutterBottom>
|
||||||
Available Prompts
|
Available Prompts
|
||||||
</Typography>
|
</Typography>
|
||||||
<List component='nav' aria-label='secondary mailbox folder'>
|
<List component='nav' aria-label='secondary mailbox folder'>
|
||||||
{prompts.map((item, index) => (
|
{availablePrompNameList.map((item, index) => (
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
key={item.id}
|
key={item.id}
|
||||||
selected={item.id === selectedPrompt?.id}
|
selected={item.id === selectedPrompt?.id}
|
||||||
@@ -321,25 +336,21 @@ const PromptLangsmithHubDialog = ({ promptType, show, onCancel }) => {
|
|||||||
</Grid>
|
</Grid>
|
||||||
<Grid xs={8} item style={{ textAlign: 'left' }}>
|
<Grid xs={8} item style={{ textAlign: 'left' }}>
|
||||||
<Box sx={{ width: '100%' }} style={{ display: 'flex', flexDirection: 'column' }}>
|
<Box sx={{ width: '100%' }} style={{ display: 'flex', flexDirection: 'column' }}>
|
||||||
<Card variant='outlined' style={{ height: 80, overflow: 'auto' }}>
|
<Card variant='outlined' style={{ height: 100, overflow: 'auto' }}>
|
||||||
<CardContent sx={{ p: 1 }}>
|
<CardContent sx={{ p: 1 }}>
|
||||||
<Typography sx={{ fontSize: 10 }} color='text.secondary' gutterBottom>
|
<Typography sx={{ fontSize: 10 }} color='text.secondary' gutterBottom>
|
||||||
Description
|
Description
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography color='text.primary'>{selectedPrompt?.description}</Typography>
|
<Typography sx={{ fontSize: 12 }} color='text.primary'>
|
||||||
</CardContent>
|
{selectedPrompt?.description}
|
||||||
</Card>
|
<br />
|
||||||
<Card sx={{ mt: 1 }} variant='outlined' style={{ height: 60, overflow: 'auto' }}>
|
{selectedPrompt?.tags?.map((item) => (
|
||||||
<CardContent sx={{ p: 1 }}>
|
<Chip size='small' key={item.id} label={item} sx={{ mr: 1, mb: 0.5, fontSize: 10 }} />
|
||||||
<Typography sx={{ fontSize: 10 }} color='text.secondary' gutterBottom>
|
))}
|
||||||
Tags
|
|
||||||
</Typography>
|
</Typography>
|
||||||
{selectedPrompt?.tags?.map((item) => (
|
|
||||||
<Chip size='small' key={item} label={item} sx={{ mr: 1, mb: 1, fontSize: 10 }} />
|
|
||||||
))}
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
<Card sx={{ mt: 1 }} variant='outlined' style={{ height: 280, overflow: 'auto' }}>
|
<Card sx={{ mt: 1 }} variant='outlined' style={{ height: 100, overflow: 'auto' }}>
|
||||||
<CardContent sx={{ p: 1 }}>
|
<CardContent sx={{ p: 1 }}>
|
||||||
<Typography sx={{ fontSize: 10 }} color='text.secondary' gutterBottom>
|
<Typography sx={{ fontSize: 10 }} color='text.secondary' gutterBottom>
|
||||||
Readme
|
Readme
|
||||||
@@ -349,6 +360,20 @@ const PromptLangsmithHubDialog = ({ promptType, show, onCancel }) => {
|
|||||||
</ReactMarkdown>
|
</ReactMarkdown>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
<Card sx={{ mt: 1 }} variant='outlined' style={{ height: 250, overflow: 'auto' }}>
|
||||||
|
<CardContent sx={{ p: 1 }}>
|
||||||
|
{selectedPrompt?.detailed?.map((item) => (
|
||||||
|
<>
|
||||||
|
<Typography sx={{ fontSize: 10 }} color='text.secondary' gutterBottom>
|
||||||
|
{item.type}
|
||||||
|
</Typography>
|
||||||
|
<ReactMarkdown key={item} sx={{ fontSize: 12, mb: 2 }} color='text.primary'>
|
||||||
|
{item.template}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
Reference in New Issue
Block a user