UNPKG

agenty

Version:

🤵 Agently is a framework helps developers to create amazing LLM based applications. 🎭 You can use it to create an LLM bansed agent instance with role set and memory easily. ⚙️ You can use Agently agent instance just like an async function and put it any

299 lines (271 loc) 10.4 kB
const EventEmitter = require('events') const findJSONString = require('./format').findJSONString class LLMManage { constructor (LLM) { this.llmList = LLM.llmList this.debug = LLM.debug this.runtime = {} } name (llmName) { this.runtime.name = llmName return this } url (apiUrl) { this.runtime.url = apiUrl return this } proxy (proxyInfo) { if (proxyInfo.host && proxyInfo.port) { this.runtime.proxy = { host: proxyInfo.host, port: proxyInfo.port } } return this } defaultOptions (defaultOptions) { this.runtime.defaultOptions = defaultOptions return this } defaultMaxContextLength (defaultMaxContextLength) { this.runtime.defaultMaxContextLength = defaultMaxContextLength return this } request (requestMethod) { this.runtime.request = requestMethod return this } extractResponse (extractResponseHandler) { this.runtime.extractResponse = extractResponseHandler return this } streaming (streamingMethod) { this.runtime.streaming = streamingMethod return this } extractStreamingData (extractStreamingDataHandler, eventMapping = { 'data': 'data' }) { this.runtime.extractStreamingData = extractStreamingDataHandler this.runtime.eventMapping = eventMapping return this } transformMessages (transformMessagesHandler) { this.runtime.transformMessages = transformMessagesHandler return this } register () { let missing = [] if (!this.runtime.name) missing.push('name') if (!this.runtime.url) missing.push('url') if (!this.runtime.request && !this.runtime.streaming) missing.push('request or streaming') if (missing.length === 0) { this.llmList[this.runtime.name] = { ...this.runtime } this.runtime = {} return { status: 200 } } else { const msg = `[LLM Register] Failed: missing ${ JSON.stringify(missing) }` this.debug(msg) this.runtime = {} return { status: 400, msg: msg } } } update () { if (!this.runtime.name) { const msg = `[LLM Update] Failed: Update require model name` this.debug(msg) return { status: 400, msg: msg } } if (!this.llmList[this.runtime.name]) { const msg = `[LLM Update] Failed: Can not find model "${ this.runtime.name }"` this.debug(msg) return { status: 400, msg: msg } } for (let key in this.runtime) { this.llmList[this.runtime.name][key] = this.runtime[key] } this.runtime = {} return { status: 200 } } } class LLMRequest { constructor (llmName, LLM) { this.debug = LLM.debug this.Options = LLM.Options this.llm = LLM.llmList[llmName] this.auth = undefined this.proxy = undefined this.retryTimes = this.Options.get('retryTimes') this.retryPause = this.Options.get('retryPause') } setAuth (auth) { this.auth = auth return this } setProxy (proxy) { this.proxy = proxy return this } setRetryTimes (retryTimes) { this.retryTimes = retryTimes return this } setRetryPause (retryPause) { this.retryPause = retryPause return this } async _request (messages, options, type) { let methodName, transform, request, extract //Request method with retry const __request = (request, reqData, retryTimes = 1) => { return new Promise( async (resolve, reject) => { try { const response = await request(reqData) resolve({ status: 200, data: response, }) } catch (e) { this.debug(`[Request Failed]\tLLM Name: ${ this.llm.name }\tError: ${ JSON.stringify(e.message) }\tRequest Data: ${ JSON.stringify(reqData) }\tRetry Times: ${ retryTimes }/${ this.retryTimes }\t Pause: ${ this.retryPause }`) if (retryTimes < this.retryTimes) { retryTimes++ setTimeout( async () => { const result = await __request(request, reqData, retryTimes) resolve(result) }, this.retryPause) } else { return { status: 400, msg: e.message } } } } ) } if (type === 'request') { //Check if request is existed if (!this.llm.request) { const msg = `[Request Failed]\tLLM Name: ${ this.llm.name }\tError: requestMethod is not registered.` this.debug(msg) return { status: 400, msg: msg } } methodName = 'Request' transform = this.llm.transformMessages request = this.llm.request extract = this.llm.extractResponse } else if (type === 'streaming') { //Check if streamMethod is existed if (!this.llm.streaming) { const msg = `[Streaming Failed]\tLLM Name: ${ this.llm.name }\tError: streaming is not registered.` this.debug(msg) return { status: 400, msg: msg } } methodName = 'Streaming' transform = this.llm.transformMessages request = this.llm.streaming extract = this.llm.extractStreamingData } //Generate request options let requestOptions = this.llm.defaultOptions if (options) { for (let key in options) { requestOptions[key] = options[key] } } //Request with retry messages = transform ? transform(messages) : messages this.debug(`[${ methodName } Messages]\t${ JSON.stringify(messages) }`) const responseResult = await __request( request, { messages: messages, url: this.llm.url, options: requestOptions, auth: this.auth || this.llm.auth || this.Options.get('auth')[this.llm.name] || '', proxy: this.proxy || this.llm.proxy || this.Options.get('proxy') || undefined, } ) if (type === 'request') { //Extract response result const finalResult = extract ? extract(responseResult) : responseResult.data return finalResult } else if (type === 'streaming') { //Extract stream data const finalResult = new EventEmitter() let streamingBuffer = '', role = '' responseResult.data.on(this.llm.eventMapping['data'], data => { //console.log(data.toString()) if (data && data.toString() && data.toString() !== '') { let JSONData = findJSONString(data.toString()) if (!JSON.parse(JSONData).emptyChunk) { let extractResult = extract ? extract(JSONData) : JSON.parse(JSONData) if (!extractResult || !extractResult?.type) extractResult = { type: 'data', data: '' } const sendResult = { type: extractResult.type, data: extractResult.data, } if (sendResult.type !== 'done') { if (typeof(sendResult?.data?.delta?.role) === 'string') role = sendResult?.data.delta.role if (sendResult?.data?.delta?.content) { const delta = sendResult.data.delta.content.toString() streamingBuffer += delta if (delta.length > 10) { for (let i = 0; i < delta.length; i += 10) { let tempResult = { type: sendResult.type, data: sendResult.data, } tempResult.data.delta.content = delta.substr(i, 10) finalResult.emit(tempResult.type, tempResult.data) } } else { finalResult.emit(sendResult.type, sendResult.data) } } } else { finalResult.emit('finish', { role: role, content: streamingBuffer }) } } } }) return finalResult } } async request (messages, options) { return await this._request(messages, options, 'request') } async streaming (messages, options) { return await this._request(messages, options, 'streaming') } } class LLM { constructor (Agently) { this.Options = Agently.Options this.debug = Agently.debug this.llmList = {} this.Manage = new LLMManage(this) this.Request = (llmName) => new LLMRequest(llmName, this) } setAuth (llmName, auth) { if (this.llmList[llmName]) { this.llmList[llmName].auth = auth } else { this.llmList[llmName] = { auth: auth } } return this } setProxy (llmName, proxy) { if (this.llmList[llmName]) { this.llmList[llmName].proxy = proxy } else { this.llmList[llmName] = { proxy: proxy } } return this } } module.exports = LLM