Last active
June 29, 2024 15:02
-
-
Save ivansglazunov/3ac187de0bb09449da8207136e1d9843 to your computer and use it in GitHub Desktop.
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
async ({ data: { newLink: replyLink, triggeredByLinkId }, deep, require }) => { | |
console.log({triggeredByLinkId}) | |
const startTime = Date.now(); | |
const PACKAGE_NAME = `@deep-foundation/chatgpt-azure`; | |
const axios = require('axios'); | |
const { Configuration, OpenAIApi } = require('openai'); | |
const chatGPTTypeLinkId = await deep.id(PACKAGE_NAME, 'ChatGPT'); | |
const conversationTypeLinkId = await deep.id(PACKAGE_NAME, 'Conversation'); | |
const apiKeyTypeLinkId = await deep.id('@deep-foundation/openai', 'ApiKey'); | |
const usesApiKeyTypeLinkId = await deep.id('@deep-foundation/openai', 'UsesApiKey'); | |
const modelTypeLinkId = await deep.id('@deep-foundation/openai', 'Model'); | |
const usesModelTypeLinkId = await deep.id('@deep-foundation/openai', 'UsesModel'); | |
const messageTypeLinkId = await deep.id('@deep-foundation/messaging', 'Message'); | |
const replyTypeLinkId = await deep.id('@deep-foundation/chatgpt-azure', 'Reply'); | |
const authorTypeLinkId = await deep.id('@deep-foundation/messaging', 'Author'); | |
const containTypeLinkId = await deep.id('@deep-foundation/core', 'Contain'); | |
const messagingTreeId = await deep.id('@deep-foundation/messaging', 'messagingTree'); | |
const systemTypeLinkId = await deep.id('@deep-foundation/chatgpt-azure', 'System'); | |
const tokensTypeLinkId = await deep.id("@deep-foundation/tokens", "Tokens") | |
const providedByTypeLinkId = await deep.id("@deep-foundation/chatgpt-azure", "ProvidedBy") | |
const reservedIds = await deep.reserve(1); | |
const chatGPTMessageLinkId = reservedIds.pop(); | |
let systemMessageId; | |
let model; | |
let systemMessage; | |
await deep.await(replyLink.from_id) | |
const { data: [messageLink] } = await deep.select({ | |
id: replyLink.from_id, | |
_not: { | |
out: { | |
to_id: chatGPTTypeLinkId, | |
type_id: authorTypeLinkId, | |
}, | |
}, | |
}); | |
if (!messageLink) { | |
return 'No need to react to message of this reply.'; | |
} | |
if (!messageLink.value?.value) { | |
throw new Error(`Message ##${messageLink.id} must have a value`); | |
} | |
const message = messageLink.value.value; | |
const apiKeyLink = await getTokenLink(); | |
const apiKey = apiKeyLink.value.value; | |
const configuration = new Configuration({ | |
apiKey: apiKey, | |
}); | |
const openai = new OpenAIApi(configuration); | |
const { data: conversationLink } = await deep.select({ | |
tree_id: { _eq: messagingTreeId }, | |
parent: { type_id: { _in: [conversationTypeLinkId, messageTypeLinkId] } }, | |
link: { id: { _eq: replyLink.from_id } }, | |
}, { | |
table: 'tree', | |
variables: { order_by: { depth: "asc" } }, | |
returning: ` | |
id | |
depth | |
root_id | |
parent_id | |
link_id | |
parent { | |
id | |
from_id | |
type_id | |
to_id | |
value | |
author: out (where: { type_id: { _eq: ${authorTypeLinkId}} }) { | |
id | |
from_id | |
type_id | |
to_id | |
} | |
tokens: out (where: { type_id: { _eq: ${tokensTypeLinkId}} }) { | |
id | |
from_id | |
type_id | |
to_id | |
value | |
} | |
}` | |
}) | |
if (!conversationLink) { | |
throw new Error('A conversationLink link is not found'); | |
} | |
const currentConversation = conversationLink.find( | |
(link) => link.parent.type_id === conversationTypeLinkId | |
); | |
conversationLink.forEach((link) => { | |
if (link.parent.author && link.parent.author.length > 0) { | |
link.parent.author = link.parent.author[0]; | |
} | |
}); | |
const { | |
data: [linkedModel], | |
} = await deep.select({ | |
type_id: modelTypeLinkId, | |
in: { | |
type_id: usesModelTypeLinkId, | |
from_id: currentConversation.parent.id, | |
}, | |
}); | |
const usedModelSelectData = { | |
type_id: modelTypeLinkId, | |
in: { | |
type_id: usesModelTypeLinkId, | |
from_id: triggeredByLinkId, | |
}, | |
} | |
const { | |
data: [userLinkedModel], | |
} = await deep.select(usedModelSelectData, { | |
returning: ` | |
id | |
value | |
in(where: { type_id: { _eq: ${usesModelTypeLinkId} } }) { | |
id | |
type_id | |
from_id | |
to_id | |
value | |
} | |
tokens: out(where: { type_id: { _eq: ${tokensTypeLinkId} } }) { | |
id | |
from_id | |
to_id | |
value | |
} | |
endpointLink: out(where: { type_id: { _eq: ${providedByTypeLinkId} } }) { | |
id | |
from_id | |
to_id | |
endpoint: to { | |
id | |
value | |
} | |
} | |
` | |
}); | |
if(!userLinkedModel) { | |
throw new Error(`Failed to find used model. Select data: ${JSON.stringify(usedModelSelectData, null, 2)}`) | |
} | |
let MAX_TOKENS | |
console.log({userLinkedModel}) | |
if (!userLinkedModel.tokens?.[0]?.value?.value) { | |
throw new Error("Token limit for the model is not specified."); | |
} else if (userLinkedModel.tokens?.[0]?.value?.value) { | |
MAX_TOKENS = userLinkedModel.tokens[0].value.value; | |
} | |
if (!userLinkedModel.endpointLink?.[0]?.endpoint?.value?.value) { | |
throw new Error("Endpoint for the model is not specified."); | |
} | |
const API_ENDPOINT = userLinkedModel.endpointLink[0].endpoint.value?.value; | |
if (!linkedModel && !userLinkedModel) { | |
throw new Error("Before using azure-GPT, create a model, tokens, and an endpoint links."); | |
} else if ( | |
(linkedModel && | |
linkedModel.value?.value && | |
userLinkedModel && | |
userLinkedModel.value?.value) || | |
(linkedModel && linkedModel.value?.value) | |
) { | |
model = linkedModel.value.value; | |
} else { | |
if (!userLinkedModel) { | |
throw new Error(`A link with type ##${userLinkedModel} is not found`); | |
} | |
if (!userLinkedModel.value?.value) { | |
throw new Error(`Linked model with user ##${userLinkedModel.id} must have a value`); | |
} else { | |
model = userLinkedModel.value.value; | |
} | |
} | |
const messageLinks = conversationLink | |
.map(item => item.parent) | |
.filter(link => link && link.type_id === messageTypeLinkId); | |
let allMessages = await getMessages({ messageLinks }); | |
const messagesToSend = [...allMessages]; | |
const { data: userLinkedSystemMessageLinks } = await deep.select({ | |
type_id: systemTypeLinkId, | |
to_id: triggeredByLinkId, | |
}, { returning: `id message: from{ id value} conversation:to{id}` }); | |
const { data: conversationLinkedSystemMessageLink } = await deep.select({ | |
type_id: systemTypeLinkId, | |
to_id: currentConversation.parent.id, | |
}, { returning: `id message: from{ id value} conversation:to{id}` }); | |
if (conversationLinkedSystemMessageLink && conversationLinkedSystemMessageLink.length > 0) { | |
const systemMessageLink = conversationLinkedSystemMessageLink[0]; | |
if (!systemMessageLink.message?.value?.value) { | |
throw new Error(`System message with link to conversation ##${systemMessageLink.id} must have a value`); | |
} else { | |
systemMessage = systemMessageLink.message.value.value; | |
systemMessageId = systemMessageLink.message; | |
await deep.await(systemMessageId.id) | |
} | |
} else if (userLinkedSystemMessageLinks && userLinkedSystemMessageLinks.length > 0) { | |
if (userLinkedSystemMessageLinks.length > 1) { | |
throw new Error("Multiple system messages linked to the user are found"); | |
} | |
const userLinkedSystemMessageLink = userLinkedSystemMessageLinks[0]; | |
if (!userLinkedSystemMessageLink.message?.value?.value) { | |
throw new Error(`System message with link to user ##${userLinkedSystemMessageLink.id} must have a value`); | |
} else { | |
systemMessage = userLinkedSystemMessageLink.message.value.value; | |
systemMessageId = userLinkedSystemMessageLink.message; | |
await deep.await(systemMessageId.id) | |
} | |
} | |
if (systemMessage) { | |
const { data: tokensLinkedToSystemMessage } = await deep.select({ | |
type_id: tokensTypeLinkId, | |
from_id: systemMessageId.id, | |
to_id: systemMessageId.id, | |
}); | |
if (!tokensLinkedToSystemMessage[0]?.value?.value) { | |
throw new Error(`System message does not contain tokens`); | |
} else if (tokensLinkedToSystemMessage[0]?.value?.value) { | |
let tokenCount = tokensLinkedToSystemMessage[0].value?.value; | |
messagesToSend.unshift({ | |
role: "system", | |
content: systemMessage, | |
tokens: tokenCount, | |
}); | |
} | |
} | |
const tokenLimit = MAX_TOKENS * 7 / 8; | |
let totalTokens = 0; | |
let messagesToSendToOpenAI = []; | |
for (let i = messagesToSend.length - 1; i >= 0; i--) { | |
const message = messagesToSend[i]; | |
console.log({message}) | |
if (message.role === 'system' || totalTokens + message.tokens <= tokenLimit) { | |
messagesToSendToOpenAI.unshift({ role: message.role, content: message.content }); | |
totalTokens += message.tokens; | |
} else { | |
break; | |
} | |
} | |
let response; | |
try { | |
response = await axios.post(API_ENDPOINT, { | |
messages: [...messagesToSendToOpenAI] | |
}, { | |
headers: { | |
'Content-Type': 'application/json', | |
'api-key': apiKey | |
} | |
}); | |
} catch (error) { | |
console.error('Error making API call:', error.response ? error.response.data : error.message); | |
} | |
const content = response.data.choices[0]?.message?.content; | |
await deep.serial({ | |
operations: [ | |
{ | |
table: 'links', | |
type: 'insert', | |
objects: { | |
id: chatGPTMessageLinkId, | |
type_id: messageTypeLinkId, | |
out: { | |
data: [ | |
{ | |
type_id: authorTypeLinkId, | |
to_id: chatGPTTypeLinkId, | |
}, | |
], | |
}, | |
}, | |
}, | |
{ | |
table: 'strings', | |
type: 'insert', | |
objects: { | |
link_id: chatGPTMessageLinkId, | |
value: content | |
} | |
}, | |
{ | |
table: 'links', | |
type: 'insert', | |
objects: { | |
type_id: replyTypeLinkId, | |
from_id: chatGPTMessageLinkId, | |
to_id: replyLink.from_id, | |
}, | |
}, | |
], | |
}); | |
async function getMessages({ messageLinks }) { | |
return Promise.all( | |
messageLinks.map(async (link) => { | |
const tokens = link.tokens?.length > 0 ? link.tokens[0].value.value : undefined; | |
return { | |
role: await getMessageRole({ messageLink: link }), | |
content: link.value.value, | |
tokens: tokens, | |
} | |
}) | |
); | |
} | |
async function getMessageRole({ messageLink }) { | |
const authorLink = messageLink.author; | |
if (!authorLink) { | |
throw new Error(`Author link not found for message ##${messageLink.id}`); | |
} | |
return authorLink.to_id === chatGPTTypeLinkId ? 'assistant' : 'user'; | |
} | |
async function getTokenLink() { | |
let resultTokenLink; | |
const { data } = await deep.select({ | |
_or: [ | |
{ | |
type_id: apiKeyTypeLinkId, | |
in: { | |
type_id: containTypeLinkId, | |
from_id: triggeredByLinkId, | |
}, | |
}, | |
{ | |
from_id: triggeredByLinkId, | |
type_id: usesApiKeyTypeLinkId, | |
}, | |
{ | |
type_id: apiKeyTypeLinkId, | |
in: { | |
from_id: triggeredByLinkId, | |
type_id: usesApiKeyTypeLinkId, | |
}, | |
}, | |
], | |
}) | |
console.log("FREEPHOENIX888.getTokenLink", {data}) | |
if (data.length === 0) { | |
throw new Error(`ApiKey ##${apiKeyTypeLinkId} is not found`); | |
} | |
const usesLinks = data.filter( | |
(link) => link.type_id === usesApiKeyTypeLinkId | |
); | |
if (usesLinks.length > 1) { | |
throw new Error( | |
`More than 1 links of type ##${usesApiKeyTypeLinkId} are found` | |
); | |
} | |
const usesLink = data.find( | |
(link) => link.type_id === usesApiKeyTypeLinkId | |
); | |
if (usesLink) { | |
const tokenLink = data.find((link) => link.id === usesLink.to_id); | |
if (!tokenLink) { | |
throw new Error(`ApiKey ##${apiKeyTypeLinkId} is not found`); | |
} else { | |
resultTokenLink = tokenLink; | |
} | |
} else { | |
const tokenLink = data.filter( | |
(link) => link.type_id === apiKeyTypeLinkId | |
); | |
if (tokenLink.length > 1) { | |
throw new Error( | |
`For 2 or more ApiKey ##${apiKeyTypeLinkId} links you must activate it with usesOpenAiApiKey link` | |
); | |
} else { | |
const tokenLink = data.find( | |
(link) => link.type_id === apiKeyTypeLinkId | |
); | |
if (!tokenLink) { | |
throw new Error(`ApiKey ##${apiKeyTypeLinkId} is not found`); | |
} | |
resultTokenLink = tokenLink; | |
} | |
} | |
if (!resultTokenLink.value?.value) { | |
throw new Error(`ApiKey ##${apiKeyTypeLinkId} has no value`); | |
} | |
return resultTokenLink; | |
} | |
const endTime = Date.now(); | |
const duration = (endTime - startTime) / 1000; | |
return { | |
request: { | |
model: model, | |
messages: [ | |
...messagesToSendToOpenAI | |
], | |
}, | |
response: response.data, | |
duration: duration, | |
totalTokens: totalTokens | |
}; | |
}; |
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
async ({ data: { newLink: reply, triggeredByLinkId }, deep, require }) => { | |
const ml = deep.minilinks; | |
const startTime = Date.now(); | |
const axios = require('axios'); | |
const ChatGPT = await deep.id(deep.linkid, 'ChatGPT'); | |
const Conversation = await deep.id(deep.linkid, 'Conversation'); | |
const ApiKey = await deep.id('@deep-foundation/openai', 'ApiKey'); | |
const UsesApiKey = await deep.id('@deep-foundation/openai', 'UsesApiKey'); | |
const Model = await deep.id('@deep-foundation/openai', 'Model'); | |
const UsesModel = await deep.id('@deep-foundation/openai', 'UsesModel'); | |
const Message = await deep.id('@deep-foundation/messaging', 'Message'); | |
const Reply = await deep.id('@deep-foundation/chatgpt-azure', 'Reply'); | |
const Author = await deep.id('@deep-foundation/messaging', 'Author'); | |
const messagingTree = await deep.id('@deep-foundation/messaging', 'messagingTree'); | |
const System = await deep.id('@deep-foundation/chatgpt-azure', 'System'); | |
const Tokens = await deep.id("@deep-foundation/tokens", "Tokens") | |
const ProvidedBy = await deep.id("@deep-foundation/chatgpt-azure", "ProvidedBy") | |
await deep.await(reply.from_id); | |
const { data: [message] } = await deep.select({ | |
id: reply.from_id, | |
_not: { out: { to_id: ChatGPT, type_id: Author } }, | |
}); | |
if (!message) return 'No need to react to message of this reply.'; | |
if (!message.value?.value) throw new Error(`Message ##${message.id} must have a value`); | |
ml.apply(await deep.select({ | |
type_id: ApiKey, | |
in: { from_id: triggeredByLinkId, type_id: UsesApiKey }, | |
return: { | |
uses: { relation: 'in', type_id: UsesApiKey }, | |
}, | |
}), 'apikey'); | |
const apikeys = ml.select({ type_id: ApiKey }); | |
const useses = ml.select({ tyoe_id: UsesApiKey, to: { type_id: ApiKey } }); | |
if (!apikeys.length) throw new Error(`ApiKey not founded`); | |
if (useses.length > 1) throw new Error(`More than 1 links of type UsesApiKey are found`); | |
if (!apikeys[0]?.value?.value) throw new Error(`ApiKey has no value`); | |
const { data: [conversation] } = await deep.select({ | |
type_id: Conversation, | |
down: { tree_id: messagingTree, link_id: reply.id } | |
}); | |
if (!conversation) return 'Conversation upper not founded.'; | |
const messagesByReply = await deep.select({ | |
type_id: Message, | |
down: { tree_id: messagingTree, link_id: reply.from_id }, | |
order_by: { id: 'desc' }, | |
return: { | |
authors: { relation: 'out', type_id: Author }, | |
tokens: { relation: 'out', type_id: Tokens }, | |
} | |
}); | |
ml.apply(messagesByReply, 'messagesByReply'); | |
const messagesById = await deep.select({ | |
id: { _nin: messagesByReply.data.map(l => l.id) }, | |
type_id: Message, | |
up: { tree_id: messagingTree, parent_id: conversation.id }, | |
order_by: { id: 'desc' }, | |
return: { | |
authors: { relation: 'out', type_id: Author }, | |
tokens: { relation: 'out', type_id: Tokens }, | |
} | |
}); | |
ml.apply(messagesById, 'messagesById'); | |
const messagesToSend = [...messagesByReply.data, messagesById.data]; | |
const models = await deep.select({ | |
type_id: Model, | |
in: { type_id: UsesModel, from_id: { _in: [conversation.id, triggeredByLinkId] } }, | |
return: { | |
usesModel: { relation: 'in', type_id: UsesModel }, | |
tokens: { relation: 'out', type_id: Tokens }, | |
providedBy: { | |
relation: 'out', type_id: ProvidedBy, | |
return: { | |
endpoint: { relation: 'to' } | |
}, | |
}, | |
}, | |
}); | |
ml.apply(models, 'models'); | |
const [userLinkedModel] = ml.select({ type_id: Model, in: { type_id: UsesModel, from_id: triggeredByLinkId } }); | |
if(!userLinkedModel) throw new Error(`UsersModel from triggeredByLinkId to Model not founded.`); | |
const [userLinkedModelTokens] = ml.select({ type_id: Tokens, from_id: userLinkedModel?.id }); | |
const [linkedModel] = ml.select({ type_id: Model, in: { type_id: UsesModel, from_id: conversation.id } }); | |
const MAX_TOKENS = userLinkedModelTokens.value.value; | |
if (!MAX_TOKENS) throw new Error("MAX_TOKENS Tokens .value from model not founded."); | |
const API_ENDPOINT = ml.select({ in: { type_id: ProvidedBy } })[0]?.value?.value; | |
if (!API_ENDPOINT) throw new Error("API_ENDPOINT Endpoint instance.value nout founded."); | |
const model = linkedModel.value.value || userLinkedModel.value.value; | |
if(!model) throw new Error(`UsersModel from conversation.id to Model not founded.`); | |
let preparedMessages = messagesToSend.map((link) => { | |
const tokens = ml.select({ type_id: Tokens, from_id: link.id })[0]?.value?.value; | |
return { | |
role: ml.select({ type_id: Author, from_id: link.id })[0]?.to_id === ChatGPT ? 'assistant' : 'user', | |
content: link.value.value, | |
tokens: ml.select({ type_id: Tokens, from_id: link.id })[0]?.value?.value, | |
} | |
}); | |
ml.apply(await deep.select({ | |
type_id: System, to_id: { _in: [triggeredByLinkId, conversation.id] }, | |
return: { | |
message: { relation: 'from' }, | |
conversation: { relation: 'to' }, | |
tokens: { relation: 'in', type_id: Tokens }, | |
}, | |
}), 'systems'); | |
const conversationSystem = ml.select({ type_id: System, to_id: conversation.id })?.[0]; | |
if (!conversationSystem?.value?.value) throw new Error(`System .value to conversation not defined`); | |
const userSystems = ml.select({ type_id: System, to_id: triggeredByLinkId }); | |
if (userSystems.length > 1) throw new Error("Multiple system messages linked to the user are found"); | |
const userSystem = userSystems[0]; | |
const systemMessage = conversationSystem.from || userSystem.from; | |
if (systemMessage) { | |
if (!systemMessage?.value?.value) throw new Error(`System.from Message.value not founded`); | |
await deep.await(systemMessage.id); | |
const tokens = ml.select({ type_id: Tokens, from_id: systemMessage.id, to_id: systemMessage.id })?.[0]; | |
if (!tokens?.value?.value) throw new Error(`System message does not contain tokens`); | |
preparedMessages.unshift({ role: 'system', content: systemMessage?.value?.value, tokens: tokens?.value?.value }); | |
} | |
const tokenLimit = MAX_TOKENS * 7 / 8; | |
let totalTokens = 0; | |
let messages = []; | |
for (let i = preparedMessages.length - 1; i >= 0; i--) { | |
const message = preparedMessages[i]; | |
if (message.role === 'system' || totalTokens + message.tokens <= tokenLimit) { | |
messages.unshift({ role: message.role, content: message.content }); | |
totalTokens += message.tokens; | |
} else { | |
break; | |
} | |
} | |
let response; | |
try { | |
response = await axios.post(API_ENDPOINT, { messages }, { | |
headers: { 'Content-Type': 'application/json', 'api-key': apikeys[0]?.value?.value }, | |
}); | |
} catch (error) { | |
console.error('Error making API call:', error.response ? error.response.data : error.message); | |
} | |
const content = response.data.choices[0]?.message?.content; | |
await deep.insert({ | |
type_id: Message, | |
string: { data: { value: content } }, | |
out: { data: [ | |
{ type_id: Author, to_id: ChatGPT }, | |
{ type_id: Reply, to_id: reply.from_id }, | |
] }, | |
}); | |
const endTime = Date.now(); | |
const duration = (endTime - startTime) / 1000; | |
ml.remove(Object.keys(ml.byId)); | |
return { | |
request: { model, messages }, | |
response: response.data, | |
duration, totalTokens, | |
}; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment