Feature/seq agents (#2798)

* update build functions

* sequential agents

* update langchain to 0.2, added sequential agent nodes

* add marketplace templates

* update howto wordings

* Merge branch 'main' into feature/Seq-Agents

# Conflicts:
#	pnpm-lock.yaml

* update deprecated functions and add new sequential nodes

* add marketplace templates

* update marketplace templates, add structured output to llm node

* add multi agents template

* update llm node with bindmodels

* update cypress version

* update templates sticky note wordings

* update tool node to include human in loop action

* update structured outputs error from models

* update cohere package to resolve google genai pipeThrough bug

* update mistral package version, added message reconstruction before invoke seq agent

* add HITL to agent

* update state messages restructuring

* update load and split methods for s3 directory
This commit is contained in:
Henry Heng
2024-07-22 17:46:14 +01:00
committed by GitHub
parent 34d0e4302c
commit bca4de0c63
152 changed files with 55307 additions and 35236 deletions
@@ -1,68 +0,0 @@
import { BaseCache } from '@langchain/core/caches'
import { BaseLLMParams } from '@langchain/core/language_models/llms'
import { NIBittensorLLM, BittensorInput } from 'langchain/experimental/llms/bittensor'
import { INode, INodeData, INodeParams } from '../../../src/Interface'
import { getBaseClasses } from '../../../src/utils'
class Bittensor_LLMs implements INode {
label: string
name: string
version: number
type: string
icon: string
category: string
description: string
baseClasses: string[]
inputs: INodeParams[]
constructor() {
this.label = 'NIBittensorLLM'
this.name = 'NIBittensorLLM'
this.version = 2.0
this.type = 'Bittensor'
this.icon = 'NIBittensor.svg'
this.category = 'LLMs'
this.description = 'Wrapper around Bittensor subnet 1 large language models'
this.baseClasses = [this.type, ...getBaseClasses(NIBittensorLLM)]
this.inputs = [
{
label: 'Cache',
name: 'cache',
type: 'BaseCache',
optional: true
},
{
label: 'System prompt',
name: 'system_prompt',
type: 'string',
additionalParams: true,
optional: true
},
{
label: 'Top Responses',
name: 'topResponses',
type: 'number',
step: 1,
optional: true,
additionalParams: true
}
]
}
async init(nodeData: INodeData, _: string): Promise<any> {
const system_prompt = nodeData.inputs?.system_prompt as string
const topResponses = Number(nodeData.inputs?.topResponses as number)
const cache = nodeData.inputs?.cache as BaseCache
const obj: Partial<BittensorInput> & BaseLLMParams = {
systemPrompt: system_prompt,
topResponses: topResponses
}
if (cache) obj.cache = cache
const model = new NIBittensorLLM(obj)
return model
}
}
module.exports = { nodeClass: Bittensor_LLMs }
@@ -1 +0,0 @@
<svg width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M18.64 25.698V29H8l1.61-6.25a9.81 9.81 0 0 0-.045-4.5C9.027 15.808 8 15.394 8 10.824c.01-2.35.916-4.601 2.517-6.256C12.12 2.913 14.285 1.989 16.54 2c2.254.01 4.412.955 5.999 2.625 1.587 1.67 2.472 3.93 2.462 6.28V12l2 4h-2v4.208a3.821 3.821 0 0 1-1.08 2.373 3.531 3.531 0 0 1-2.306 1.054c-.165.01-.375.004-.606-.012-1.242-.085-2.367.83-2.367 2.075Z" fill="#000" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M21 13h-2l-1-2m3-1-1-2h-4m-3 1 2 4m-1 6 3-3h4" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>

Before

Width:  |  Height:  |  Size: 666 B

@@ -1,7 +1,7 @@
import { BaseCache } from '@langchain/core/caches'
import { Cohere, CohereInput } from '@langchain/cohere'
import { ICommonObject, INode, INodeData, INodeOptionsValue, INodeParams } from '../../../src/Interface'
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
import { Cohere, CohereInput } from './core'
import { getModels, MODEL_TYPE } from '../../../src/modelLoader'
class Cohere_LLMs implements INode {
@@ -1,78 +0,0 @@
import { LLM, BaseLLMParams } from '@langchain/core/language_models/llms'
export interface CohereInput extends BaseLLMParams {
/** Sampling temperature to use */
temperature?: number
/**
* Maximum number of tokens to generate in the completion.
*/
maxTokens?: number
/** Model to use */
model?: string
apiKey?: string
}
export class Cohere extends LLM implements CohereInput {
temperature = 0
maxTokens = 250
model: string
apiKey: string
constructor(fields?: CohereInput) {
super(fields ?? {})
const apiKey = fields?.apiKey ?? undefined
if (!apiKey) {
throw new Error('Please set the COHERE_API_KEY environment variable or pass it to the constructor as the apiKey field.')
}
this.apiKey = apiKey
this.maxTokens = fields?.maxTokens ?? this.maxTokens
this.temperature = fields?.temperature ?? this.temperature
this.model = fields?.model ?? this.model
}
_llmType() {
return 'cohere'
}
/** @ignore */
async _call(prompt: string, options: this['ParsedCallOptions']): Promise<string> {
const { cohere } = await Cohere.imports()
cohere.init(this.apiKey)
// Hit the `generate` endpoint on the `large` model
const generateResponse = await this.caller.callWithOptions({ signal: options.signal }, cohere.generate.bind(cohere), {
prompt,
model: this.model,
max_tokens: this.maxTokens,
temperature: this.temperature,
end_sequences: options.stop
})
try {
return generateResponse.body.generations[0].text
} catch {
throw new Error('Could not parse response.')
}
}
/** @ignore */
static async imports(): Promise<{
cohere: typeof import('cohere-ai')
}> {
try {
const { default: cohere } = await import('cohere-ai')
return { cohere }
} catch (e) {
throw new Error('Please install cohere-ai as a dependency with, e.g. `pnpm install cohere-ai`')
}
}
}
@@ -1,8 +1,8 @@
import { Replicate, ReplicateInput } from '@langchain/community/llms/replicate'
import { BaseCache } from '@langchain/core/caches'
import { BaseLLMParams } from '@langchain/core/language_models/llms'
import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface'
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
import { Replicate, ReplicateInput } from './core'
class Replicate_LLMs implements INode {
label: string
@@ -97,7 +97,7 @@ class Replicate_LLMs implements INode {
}
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
const modelName = nodeData.inputs?.model as string
const modelName = nodeData.inputs?.model as `${string}/${string}` | `${string}/${string}:${string}`
const temperature = nodeData.inputs?.temperature as string
const maxTokens = nodeData.inputs?.maxTokens as string
const topP = nodeData.inputs?.topP as string
@@ -107,14 +107,10 @@ class Replicate_LLMs implements INode {
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
const apiKey = getCredentialParam('replicateApiKey', credentialData, nodeData)
const version = modelName.split(':').pop()
const name = modelName.split(':')[0].split('/').pop()
const org = modelName.split(':')[0].split('/')[0]
const cache = nodeData.inputs?.cache as BaseCache
const obj: ReplicateInput & BaseLLMParams = {
model: `${org}/${name}:${version}`,
model: modelName,
apiKey
}
@@ -0,0 +1,145 @@
import { LLM, type BaseLLMParams } from '@langchain/core/language_models/llms'
import { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager'
import { GenerationChunk } from '@langchain/core/outputs'
import type ReplicateInstance from 'replicate'
export interface ReplicateInput {
model: `${string}/${string}` | `${string}/${string}:${string}`
input?: {
// different models accept different inputs
[key: string]: string | number | boolean
}
apiKey?: string
promptKey?: string
}
export class Replicate extends LLM implements ReplicateInput {
lc_serializable = true
model: ReplicateInput['model']
input: ReplicateInput['input']
apiKey: string
promptKey?: string
constructor(fields: ReplicateInput & BaseLLMParams) {
super(fields)
const apiKey = fields?.apiKey
if (!apiKey) {
throw new Error('Please set the REPLICATE_API_TOKEN')
}
this.apiKey = apiKey
this.model = fields.model
this.input = fields.input ?? {}
this.promptKey = fields.promptKey
}
_llmType() {
return 'replicate'
}
/** @ignore */
async _call(prompt: string, options: this['ParsedCallOptions']): Promise<string> {
const replicate = await this._prepareReplicate()
const input = await this._getReplicateInput(replicate, prompt)
const output = await this.caller.callWithOptions({ signal: options.signal }, () =>
replicate.run(this.model, {
input
})
)
if (typeof output === 'string') {
return output
} else if (Array.isArray(output)) {
return output.join('')
} else {
// Note this is a little odd, but the output format is not consistent
// across models, so it makes some amount of sense.
return String(output)
}
}
async *_streamResponseChunks(
prompt: string,
options: this['ParsedCallOptions'],
runManager?: CallbackManagerForLLMRun
): AsyncGenerator<GenerationChunk> {
const replicate = await this._prepareReplicate()
const input = await this._getReplicateInput(replicate, prompt)
const stream = await this.caller.callWithOptions({ signal: options?.signal }, async () =>
replicate.stream(this.model, {
input
})
)
for await (const chunk of stream) {
if (chunk.event === 'output') {
yield new GenerationChunk({ text: chunk.data, generationInfo: chunk })
await runManager?.handleLLMNewToken(chunk.data ?? '')
}
// stream is done
if (chunk.event === 'done')
yield new GenerationChunk({
text: '',
generationInfo: { finished: true }
})
}
}
/** @ignore */
static async imports(): Promise<{
Replicate: typeof ReplicateInstance
}> {
try {
const { default: Replicate } = await import('replicate')
return { Replicate }
} catch (e) {
throw new Error('Please install replicate as a dependency with, e.g. `yarn add replicate`')
}
}
private async _prepareReplicate(): Promise<ReplicateInstance> {
const imports = await Replicate.imports()
return new imports.Replicate({
userAgent: 'flowise',
auth: this.apiKey
})
}
private async _getReplicateInput(replicate: ReplicateInstance, prompt: string) {
if (this.promptKey === undefined) {
const [modelString, versionString] = this.model.split(':')
if (versionString) {
const version = await replicate.models.versions.get(modelString.split('/')[0], modelString.split('/')[1], versionString)
const openapiSchema = version.openapi_schema
const inputProperties: { 'x-order': number | undefined }[] = (openapiSchema as any)?.components?.schemas?.Input?.properties
if (inputProperties === undefined) {
this.promptKey = 'prompt'
} else {
const sortedInputProperties = Object.entries(inputProperties).sort(([_keyA, valueA], [_keyB, valueB]) => {
const orderA = valueA['x-order'] || 0
const orderB = valueB['x-order'] || 0
return orderA - orderB
})
this.promptKey = sortedInputProperties[0][0] ?? 'prompt'
}
} else {
this.promptKey = 'prompt'
}
}
return {
[this.promptKey!]: prompt,
...this.input
}
}
}