LS Prompt Hub: Moving calls to server side and adding functionality to show the detailed prompt

This commit is contained in:
vinodkiran
2023-11-27 22:42:04 +05:30
parent 4a3e1784d8
commit 8897dd9c5f
4 changed files with 144 additions and 43 deletions
+40
View File
@@ -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
// ---------------------------------------- // ----------------------------------------
+27
View File
@@ -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
}
+9
View File
@@ -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,13 +182,15 @@ 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>
<FormControl style={{ width: '30%' }} xs={4} sx={{ m: 1 }}>
<CredentialInputHandler <CredentialInputHandler
size='small'
sx={{ flexGrow: 1 }} sx={{ flexGrow: 1 }}
key={credentialId} key={credentialId}
data={credentialId ? { credential: credentialId } : {}} data={credentialId ? { credential: credentialId } : {}}
@@ -187,12 +198,13 @@ const PromptLangsmithHubDialog = ({ promptType, show, onCancel }) => {
label: 'Connect Credential', label: 'Connect Credential',
name: 'credential', name: 'credential',
type: 'credential', type: 'credential',
credentialNames: ['openAIApi'] credentialNames: ['langsmithApi']
}} }}
onSelect={(newValue) => { onSelect={(newValue) => {
setCredentialId(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' }}>
<CardContent sx={{ p: 1 }}>
<Typography sx={{ fontSize: 10 }} color='text.secondary' gutterBottom>
Tags
</Typography>
{selectedPrompt?.tags?.map((item) => ( {selectedPrompt?.tags?.map((item) => (
<Chip size='small' key={item} label={item} sx={{ mr: 1, mb: 1, fontSize: 10 }} /> <Chip size='small' key={item.id} label={item} sx={{ mr: 1, mb: 0.5, fontSize: 10 }} />
))} ))}
</Typography>
</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>