From d8c7653b0fa960ca5c61d1b60529faf6c91d3b85 Mon Sep 17 00:00:00 2001 From: charliehuang09 Date: Thu, 28 Dec 2023 07:27:02 -0800 Subject: [PATCH 001/107] convert to react Signed-off-by: charliehuang09 --- src/lib/completion.ts | 55 +++++++++++++++++++--------- src/routes/+page.svelte | 24 +++++------- src/routes/api/completion/+server.ts | 26 ++++++++----- 3 files changed, 63 insertions(+), 42 deletions(-) diff --git a/src/lib/completion.ts b/src/lib/completion.ts index 29403ae..6c675d5 100644 --- a/src/lib/completion.ts +++ b/src/lib/completion.ts @@ -8,6 +8,9 @@ import { ChatHistoryType, type ChatHistory } from '$lib/history'; import { BytesOutputParser } from 'langchain/schema/output_parser'; import { QdrantClient } from '@qdrant/js-client-rest'; +import { initializeAgentExecutorWithOptions } from 'langchain/agents'; +import { SerpAPI } from 'langchain/tools'; +import { Calculator } from 'langchain/tools/calculator'; const DEFAULT_MODEL = 'gpt-3.5-turbo'; const DEFAULT_COLLECTION = 'default'; @@ -18,6 +21,7 @@ export class ChatbotCompletion { private qdrant_client: QdrantClient; private qdrant_collection: string; + private tools: any; constructor( openai_api_key: string, @@ -29,14 +33,15 @@ export class ChatbotCompletion { qdrant_collection?: string; } ) { - this.model = new ChatOpenAI({ - openAIApiKey: openai_api_key, - temperature: 0.7, - streaming: true, - maxTokens: 250, - modelName: openai_model, - verbose: true - }); + // this.model = new ChatOpenAI({ + // openAIApiKey: openai_api_key, + // temperature: 0.7, + // streaming: true, + // maxTokens: 250, + // modelName: openai_model, + // verbose: false + // }); + this.model = new ChatOpenAI({ temperature: 0 }); this.embeddings_model = new OpenAIEmbeddings({ openAIApiKey: openai_api_key, @@ -46,6 +51,15 @@ export class ChatbotCompletion { url: 'http://' + (process.env.QDRANT_HOST ?? 'localhost') + ':6333' }); + this.tools = [ + new SerpAPI(process.env.SERPAPI_API_KEY, { + location: 'Austin,Texas,United States', + hl: 'en', + gl: 'us' + }), + new Calculator() + ]; + this.qdrant_collection = qdrant_collection; } @@ -60,7 +74,7 @@ export class ChatbotCompletion { with_vector: true }); - console.log(qdrant_results); + // console.log(qdrant_results); // eslint-disable-next-line @typescript-eslint/no-explicit-any return qdrant_results.map((result: any) => { @@ -76,7 +90,7 @@ export class ChatbotCompletion { const vector_response = await this.qdrant_similarity_search(query, 2); - console.log(vector_response); + // console.log(vector_response); console.log('Vector response retreived'); @@ -97,7 +111,17 @@ export class ChatbotCompletion { } // eslint-disable-next-line @typescript-eslint/no-explicit-any - public async query(history: ChatHistory[]): Promise { + public async query(history: ChatHistory[], input: any): Promise { + const executor = await initializeAgentExecutorWithOptions(this.tools, this.model, { + agentType: 'zero-shot-react-description', + verbose: false + }); + const result = await executor.invoke({ input }); + console.log('******') + console.log(input); + console.log(result); + return result; + const vector_response = await this.get_vector_response(history[history.length - 1].content); const context = vector_response.map((content: string) => { @@ -116,16 +140,13 @@ export class ChatbotCompletion { const chat_history = [ new SystemMessage({ content: `You are a kind, professional, understanding, and enthusiastic - assistant that is an expert in mechanical, electrical, and software engineering, but most importantly FIRST robotics - and helping all levels of frc robotics teams. Avoiding repeating the same information and useless statements. - The current date is ${new Date()}.` + assistant that is an expert in mechanical, electrical, and software engineering, but most importantly FIRST robotics + and helping all levels of frc robotics teams. Avoiding repeating the same information and useless statements. + The current date is ${new Date()}.` }), - ...context, ...this.generate_history(history) ]; - // TODO(max): Add history pruning based on token length - return await this.model.pipe(new BytesOutputParser()).stream(chat_history); } } diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index e6f55b9..71449c1 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -37,19 +37,19 @@ } ]; - userInput = ''; const response = await fetch('api/completion', { method: 'POST', body: JSON.stringify({ - chatHistory: history + chatHistory: history, + input: userInput, }), headers: { 'content-type': 'application/json' } }); + userInput = ''; - const stream = response.body; history = [ ...history, @@ -58,19 +58,13 @@ content: '' } ]; + const data = await response.json(); + let message = data.output; + console.log('%%%%%%'); + console.log(message); + console.log("^^^^"); - const decoder = new TextDecoder('utf-8'); - - for await (const chunk of streamAsyncIterator(stream!)) { - console.log(decoder.decode(chunk)); - history[history.length - 1].content += decoder.decode(chunk); - - // TODO(max): Find a way to lock the actual completion from OpenAI, right now this will still use the full tokens - if (cancelStream) { - cancelStream = false; - break; - } - } + history[history.length - 1].content = message; completionState.set(CompletionState.Completed); } diff --git a/src/routes/api/completion/+server.ts b/src/routes/api/completion/+server.ts index 692bd37..06bb151 100644 --- a/src/routes/api/completion/+server.ts +++ b/src/routes/api/completion/+server.ts @@ -1,21 +1,27 @@ +import { CONDA_DEFAULT_ENV } from '$env/static/private'; import { ChatbotCompletion } from '$lib'; //TODO(max): Change this function to another protocol, nothing is being POSTed /** @type {import('./$types').RequestHandler} */ export async function POST(params: { request: Request }) { - const { chatHistory } = await params.request.json(); + const { chatHistory, input } = await params.request.json(); const completion_manager = new ChatbotCompletion(import.meta.env.VITE_OPENAI_API_KEY!, { openai_model: import.meta.env.VITE_OPENAI_MODEL_NAME! }); + + const output = await completion_manager.query(chatHistory, input); + console.log('-------') + console.log(output); + console.log("^^^^^") - const stream = await completion_manager.query(chatHistory); - - const response = new Response(stream, { - headers: { - 'Content-Type': 'text/plain; charset=utf-8' - } - }); - - return response; + const response = new Response(output); + console.log(new Response("BRUH")); + console.log("^^^^^") + return new Response(JSON.stringify(output), { + headers: { + 'Content-Type': 'text/plain' + } + }); + return new Response(); } From 91c84f4f7e0257ce28e25d68d3d3217611ff2206 Mon Sep 17 00:00:00 2001 From: charliehuang09 Date: Thu, 28 Dec 2023 16:42:12 -0800 Subject: [PATCH 002/107] rough code for react Signed-off-by: charliehuang09 --- src/lib/completion.ts | 176 ++++++++++++++++++++------- src/routes/+page.svelte | 3 - src/routes/api/completion/+server.ts | 12 +- 3 files changed, 134 insertions(+), 57 deletions(-) diff --git a/src/lib/completion.ts b/src/lib/completion.ts index 6c675d5..18002c9 100644 --- a/src/lib/completion.ts +++ b/src/lib/completion.ts @@ -2,7 +2,7 @@ import { Document } from 'langchain/document'; import { ChatOpenAI } from 'langchain/chat_models/openai'; import { OpenAIEmbeddings } from 'langchain/embeddings/openai'; -import { SystemMessage, BaseMessage, AIMessage, HumanMessage } from 'langchain/schema'; +import { type AgentAction, type AgentFinish, SystemMessage, BaseMessage, AIMessage, HumanMessage, type InputValues, type AgentStep} from 'langchain/schema'; import { ChatHistoryType, type ChatHistory } from '$lib/history'; import { BytesOutputParser } from 'langchain/schema/output_parser'; @@ -11,6 +11,10 @@ import { QdrantClient } from '@qdrant/js-client-rest'; import { initializeAgentExecutorWithOptions } from 'langchain/agents'; import { SerpAPI } from 'langchain/tools'; import { Calculator } from 'langchain/tools/calculator'; +import { formatLogToString } from "langchain/agents/format_scratchpad/log"; +import { RunnableSequence } from "langchain/schema/runnable"; +import { PromptTemplate } from "langchain/prompts"; +import { AgentExecutor } from "langchain/agents"; const DEFAULT_MODEL = 'gpt-3.5-turbo'; const DEFAULT_COLLECTION = 'default'; @@ -22,6 +26,7 @@ export class ChatbotCompletion { private qdrant_client: QdrantClient; private qdrant_collection: string; private tools: any; + private executor: any; constructor( openai_api_key: string, @@ -33,15 +38,14 @@ export class ChatbotCompletion { qdrant_collection?: string; } ) { - // this.model = new ChatOpenAI({ - // openAIApiKey: openai_api_key, - // temperature: 0.7, - // streaming: true, - // maxTokens: 250, - // modelName: openai_model, - // verbose: false - // }); - this.model = new ChatOpenAI({ temperature: 0 }); + this.model = new ChatOpenAI({ + openAIApiKey: openai_api_key, + temperature: 0.7, + streaming: true, + maxTokens: 250, + modelName: openai_model, + verbose: false + }); this.embeddings_model = new OpenAIEmbeddings({ openAIApiKey: openai_api_key, @@ -53,16 +57,109 @@ export class ChatbotCompletion { this.tools = [ new SerpAPI(process.env.SERPAPI_API_KEY, { - location: 'Austin,Texas,United States', - hl: 'en', - gl: 'us' + location: "Austin,Texas,United States", + hl: "en", + gl: "us", }), - new Calculator() - ]; + new Calculator(), + ]; this.qdrant_collection = qdrant_collection; } + public async generate_executor(){ + this.executor = await initializeAgentExecutorWithOptions(this.tools, this.model, { + agentType: 'zero-shot-react-description', + verbose: false + }); + } + + public async formatMessages(values: InputValues): Promise> { + //From https://js.langchain.com/docs/modules/agents/how_to/custom_llm_agent + const PREFIX = `Answer the following questions as best you can. You have access to the following tools: {tools}`; + const TOOL_INSTRUCTIONS_TEMPLATE = `Use the following format in your response: + Question: the input question you must answer + Thought: you should always think about what to do + Action: the action to take, should be one of [{tool_names}] + Action Input: the input to the action + Observation: the result of the action + ... (this Thought/Action/Action Input/Observation can repeat N times) + Thought: I now know the final answer + Final Answer: the final answer to the original input question`; + const SUFFIX = `Begin! + Question: {input} + Thought:`; + console.log('checkpoint') + if (!("input" in values) || !("intermediate_steps" in values)) { + throw new Error("Missing input or agent_scratchpad from values."); + } + /** Extract and case the intermediateSteps from values as Array or an empty array if none are passed */ + const intermediateSteps = values.intermediate_steps + ? (values.intermediate_steps as Array) + : []; + /** Call the helper `formatLogToString` which returns the steps as a string */ + const agentScratchpad = formatLogToString(intermediateSteps); + /** Construct the tool strings */ + const toolStrings = this.tools + .map((tool: any) => `${tool.name}: ${tool.description}`) + .join("\n"); + const toolNames = this.tools.map((tool: any) => tool.name).join(",\n"); + /** Create templates and format the instructions and suffix prompts */ + const prefixTemplate = new PromptTemplate({ + template: PREFIX, + inputVariables: ["tools"], + }); + const instructionsTemplate = new PromptTemplate({ + template: TOOL_INSTRUCTIONS_TEMPLATE, + inputVariables: ["tool_names"], + }); + const suffixTemplate = new PromptTemplate({ + template: SUFFIX, + inputVariables: ["input"], + }); + /** Format both templates by passing in the input variables */ + const formattedPrefix = await prefixTemplate.format({ + tools: toolStrings, + }); + const formattedInstructions = await instructionsTemplate.format({ + tool_names: toolNames, + }); + const formattedSuffix = await suffixTemplate.format({ + input: values.input, + }); + /** Construct the final prompt string */ + const formatted = [ + formattedPrefix, + formattedInstructions, + formattedSuffix, + agentScratchpad, + ].join("\n"); + /** Return the message as a HumanMessage. */ + return [new HumanMessage(formatted)]; + } + private customOutputParser(text: string): AgentAction | AgentFinish { + //From https://js.langchain.com/docs/modules/agents/how_to/custom_llm_agent + /** If the input includes "Final Answer" return as an instance of `AgentFinish` */ + if (text.includes("Final Answer:")) { + const parts = text.split("Final Answer:"); + const input = parts[parts.length - 1].trim(); + const finalAnswers = { output: input }; + return { log: text, returnValues: finalAnswers }; + } + /** Use regex to extract any actions and their values */ + const match = /Action: (.*)\nAction Input: (.*)/s.exec(text); + if (!match) { + throw new Error(`Could not parse LLM output: ${text}`); + } + /** Return as an instance of `AgentAction` */ + return { + tool: match[1].trim(), + toolInput: match[2].trim().replace(/^"+|"+$/g, ""), + log: text, + }; + } + + /* We need to pass with_vector to qdrant to get our response */ @@ -112,41 +209,30 @@ export class ChatbotCompletion { // eslint-disable-next-line @typescript-eslint/no-explicit-any public async query(history: ChatHistory[], input: any): Promise { - const executor = await initializeAgentExecutorWithOptions(this.tools, this.model, { - agentType: 'zero-shot-react-description', - verbose: false + const runnable = RunnableSequence.from([ + { + input: (values: InputValues) => values.input, + intermediate_steps: (values: InputValues) => values.steps, + }, + this.formatMessages, + this.model, + this.customOutputParser, + ]); + const ex = new AgentExecutor({ + agent: runnable, + tools: this.tools, }); - const result = await executor.invoke({ input }); - console.log('******') - console.log(input); - console.log(result); - return result; - const vector_response = await this.get_vector_response(history[history.length - 1].content); + const tmp = `Who is Olivia Wilde's boyfriend? What is his current age raised to the 0.23 power?`; - const context = vector_response.map((content: string) => { - return new SystemMessage({ content }); - }); + console.log(`Executing with input "${tmp}"...`); - if (context.length == 0) { - context.push( - new SystemMessage({ - content: - 'You dont have data on this content, you may want to respond with "sorry I cannot answer that as I do not have enough information"' - }) - ); - } + const res = await ex.invoke({ tmp }); - const chat_history = [ - new SystemMessage({ - content: `You are a kind, professional, understanding, and enthusiastic - assistant that is an expert in mechanical, electrical, and software engineering, but most importantly FIRST robotics - and helping all levels of frc robotics teams. Avoiding repeating the same information and useless statements. - The current date is ${new Date()}.` - }), - ...this.generate_history(history) - ]; + console.log(`Got output ${res.output}`); - return await this.model.pipe(new BytesOutputParser()).stream(chat_history); + + const result = await this.executor.invoke({ input }); + return result; } } diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 71449c1..fc9537e 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -60,9 +60,6 @@ ]; const data = await response.json(); let message = data.output; - console.log('%%%%%%'); - console.log(message); - console.log("^^^^"); history[history.length - 1].content = message; diff --git a/src/routes/api/completion/+server.ts b/src/routes/api/completion/+server.ts index 06bb151..a13a65e 100644 --- a/src/routes/api/completion/+server.ts +++ b/src/routes/api/completion/+server.ts @@ -9,19 +9,13 @@ export async function POST(params: { request: Request }) { const completion_manager = new ChatbotCompletion(import.meta.env.VITE_OPENAI_API_KEY!, { openai_model: import.meta.env.VITE_OPENAI_MODEL_NAME! }); - - const output = await completion_manager.query(chatHistory, input); - console.log('-------') - console.log(output); - console.log("^^^^^") - const response = new Response(output); - console.log(new Response("BRUH")); - console.log("^^^^^") + await completion_manager.generate_executor(); + const output = await completion_manager.query(chatHistory, input); + return new Response(JSON.stringify(output), { headers: { 'Content-Type': 'text/plain' } }); - return new Response(); } From 08b971d8a7ea4abbb53696198d50a6a21fd5d1e2 Mon Sep 17 00:00:00 2001 From: charliehuang09 Date: Thu, 28 Dec 2023 16:42:39 -0800 Subject: [PATCH 003/107] rough code for react Signed-off-by: charliehuang09 --- src/lib/completion.ts | 213 ++++++++++++++++----------- src/routes/+page.svelte | 10 +- src/routes/api/completion/+server.ts | 18 +-- 3 files changed, 139 insertions(+), 102 deletions(-) diff --git a/src/lib/completion.ts b/src/lib/completion.ts index 6c675d5..8a29b13 100644 --- a/src/lib/completion.ts +++ b/src/lib/completion.ts @@ -2,7 +2,17 @@ import { Document } from 'langchain/document'; import { ChatOpenAI } from 'langchain/chat_models/openai'; import { OpenAIEmbeddings } from 'langchain/embeddings/openai'; -import { SystemMessage, BaseMessage, AIMessage, HumanMessage } from 'langchain/schema'; +import { + type AgentAction, + type AgentFinish, + SystemMessage, + BaseMessage, + AIMessage, + HumanMessage, + type InputValues, + type AgentStep, + AIMessageChunk +} from 'langchain/schema'; import { ChatHistoryType, type ChatHistory } from '$lib/history'; import { BytesOutputParser } from 'langchain/schema/output_parser'; @@ -11,6 +21,10 @@ import { QdrantClient } from '@qdrant/js-client-rest'; import { initializeAgentExecutorWithOptions } from 'langchain/agents'; import { SerpAPI } from 'langchain/tools'; import { Calculator } from 'langchain/tools/calculator'; +import { formatLogToString } from 'langchain/agents/format_scratchpad/log'; +import { RunnableSequence } from 'langchain/schema/runnable'; +import { PromptTemplate } from 'langchain/prompts'; +import { AgentExecutor } from 'langchain/agents'; const DEFAULT_MODEL = 'gpt-3.5-turbo'; const DEFAULT_COLLECTION = 'default'; @@ -22,6 +36,7 @@ export class ChatbotCompletion { private qdrant_client: QdrantClient; private qdrant_collection: string; private tools: any; + private executor: any; constructor( openai_api_key: string, @@ -32,16 +47,16 @@ export class ChatbotCompletion { openai_model?: string; qdrant_collection?: string; } - ) { - // this.model = new ChatOpenAI({ - // openAIApiKey: openai_api_key, - // temperature: 0.7, - // streaming: true, - // maxTokens: 250, - // modelName: openai_model, - // verbose: false - // }); - this.model = new ChatOpenAI({ temperature: 0 }); + ) + { + this.model = new ChatOpenAI({ + openAIApiKey: openai_api_key, + temperature: 0.7, + streaming: true, + maxTokens: 250, + modelName: openai_model, + verbose: false + }); this.embeddings_model = new OpenAIEmbeddings({ openAIApiKey: openai_api_key, @@ -63,90 +78,122 @@ export class ChatbotCompletion { this.qdrant_collection = qdrant_collection; } - /* - We need to pass with_vector to qdrant to get our response - */ - private async qdrant_similarity_search(query: string, k: number): Promise { - const query_embedding = await this.embeddings_model.embedQuery(query); - const qdrant_results = await this.qdrant_client.search(this.qdrant_collection, { - vector: query_embedding, - limit: k, - with_vector: true - }); - - // console.log(qdrant_results); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return qdrant_results.map((result: any) => { - return new Document({ - pageContent: result.payload.pageContent, - metadata: result.payload.metadata - }); + public async generate_executor() { + this.executor = await initializeAgentExecutorWithOptions(this.tools, this.model, { + agentType: 'zero-shot-react-description', + verbose: false }); } - - private async get_vector_response(query: string): Promise { - console.log('Retrieving vector response from qdrant...'); - - const vector_response = await this.qdrant_similarity_search(query, 2); - - // console.log(vector_response); - - console.log('Vector response retreived'); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return vector_response.map((document: Document>) => { - return document.pageContent; + private formatMessages = async (values:InputValues) => { + // private async formatMessages(values: InputValues): Promise> { + //From https://js.langchain.com/docs/modules/agents/how_to/custom_llm_agent + const PREFIX = `Answer the following questions as best you can. You have access to the following tools: {tools}.`; + const TOOL_INSTRUCTIONS_TEMPLATE = `You can use the following format in your response: + Question: the input question you must answer + Thought: you should always think about what to do + Action: the action to take, should be one of [{tool_names}] + Action Input: the input to the action + Observation: the result of the action + ... (this Thought/Action/Action Input/Observation can repeat N times) + Thought: I now know the final answer + Final Answer: the final answer to the original input question`; + const SUFFIX = `Begin! + Question: {input} + Thought:`; + if (!('input' in values) || !('intermediate_steps' in values)) { + throw new Error('Missing input or agent_scratchpad from values.'); + } + /** Extract and case the intermediateSteps from values as Array or an empty array if none are passed */ + const intermediateSteps = values.intermediate_steps + ? (values.intermediate_steps as Array) + : []; + /** Call the helper `formatLogToString` which returns the steps as a string */ + const agentScratchpad = formatLogToString(intermediateSteps); + /** Construct the tool strings */ + const toolStrings = this.tools + .map((tool: any) => `${tool.name}: ${tool.description}`) + .join('\n'); + const toolNames = this.tools.map((tool: any) => tool.name).join(',\n'); + /** Create templates and format the instructions and suffix prompts */ + const prefixTemplate = new PromptTemplate({ + template: PREFIX, + inputVariables: ['tools'] }); - } - - private generate_history(history: ChatHistory[]): BaseMessage[] { - return history.map((message: { content: string; type: ChatHistoryType }) => { - if (message.type == ChatHistoryType.AI) { - return new AIMessage({ content: message.content }); - } else { - return new HumanMessage({ content: message.content }); - } + const instructionsTemplate = new PromptTemplate({ + template: TOOL_INSTRUCTIONS_TEMPLATE, + inputVariables: ['tool_names'] + }); + const suffixTemplate = new PromptTemplate({ + template: SUFFIX, + inputVariables: ['input'] + }); + /** Format both templates by passing in the input variables */ + const formattedPrefix = await prefixTemplate.format({ + tools: toolStrings + }); + const formattedInstructions = await instructionsTemplate.format({ + tool_names: toolNames }); + const formattedSuffix = await suffixTemplate.format({ + input: values.input.text + }); + /** Construct the final prompt string */ + const formatted = [ + formattedPrefix, + formattedInstructions, + formattedSuffix, + agentScratchpad + ].join('\n'); + console.log(values) + console.log(formatted) + return [new HumanMessage(formatted)]; + } + private customOutputParser(text: AIMessageChunk): AgentAction | AgentFinish { + //From https://js.langchain.com/docs/modules/agents/how_to/custom_llm_agent + /** If the input includes "Final Answer" return as an instance of `AgentFinish` */ + if (text.lc_kwargs.content.includes('Final Answer:')) { + console.log(text.lc_kwargs.content) + const parts = text.lc_kwargs.content.split('Final Answer:'); + const input = parts[parts.length - 1].trim(); + const finalAnswers = { output: input }; + return { log: text.lc_kwargs.content, returnValues: finalAnswers }; + } + /** Use regex to extract any actions and their values */ + const match = /Action: (.*)\nAction Input: (.*)/s.exec(text.lc_kwargs.content); + if (!match) { + throw new Error(`Could not parse LLM output: ${text.lc_kwargs.content}`); + } + /** Return as an instance of `AgentAction` */ + return { + tool: match[1].trim(), + toolInput: match[2].trim().replace(/^"+|"+$/g, ''), + log: text.lc_kwargs.content + }; } - // eslint-disable-next-line @typescript-eslint/no-explicit-any public async query(history: ChatHistory[], input: any): Promise { - const executor = await initializeAgentExecutorWithOptions(this.tools, this.model, { - agentType: 'zero-shot-react-description', - verbose: false + this.formatMessages.bind(this); + const runnable = RunnableSequence.from([ + { + input: (values: InputValues) => values, + intermediate_steps: (values: InputValues) => values.steps + }, + this.formatMessages, + this.model, + this.customOutputParser + ]); + const ex = new AgentExecutor({ + agent: runnable, + tools: this.tools }); - const result = await executor.invoke({ input }); - console.log('******') - console.log(input); - console.log(result); - return result; - const vector_response = await this.get_vector_response(history[history.length - 1].content); - const context = vector_response.map((content: string) => { - return new SystemMessage({ content }); - }); + const tmp = input; - if (context.length == 0) { - context.push( - new SystemMessage({ - content: - 'You dont have data on this content, you may want to respond with "sorry I cannot answer that as I do not have enough information"' - }) - ); - } + console.log(`Executing with input "${tmp}"...`); - const chat_history = [ - new SystemMessage({ - content: `You are a kind, professional, understanding, and enthusiastic - assistant that is an expert in mechanical, electrical, and software engineering, but most importantly FIRST robotics - and helping all levels of frc robotics teams. Avoiding repeating the same information and useless statements. - The current date is ${new Date()}.` - }), - ...this.generate_history(history) - ]; + const res = await ex.invoke({ text: tmp }); - return await this.model.pipe(new BytesOutputParser()).stream(chat_history); + return res; } } diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 71449c1..79be914 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -37,12 +37,11 @@ } ]; - const response = await fetch('api/completion', { method: 'POST', body: JSON.stringify({ chatHistory: history, - input: userInput, + input: userInput }), headers: { 'content-type': 'application/json' @@ -50,7 +49,6 @@ }); userInput = ''; - history = [ ...history, { @@ -59,10 +57,8 @@ } ]; const data = await response.json(); - let message = data.output; - console.log('%%%%%%'); - console.log(message); - console.log("^^^^"); + console.log(data) + let message = data.output; history[history.length - 1].content = message; diff --git a/src/routes/api/completion/+server.ts b/src/routes/api/completion/+server.ts index 06bb151..2aebfee 100644 --- a/src/routes/api/completion/+server.ts +++ b/src/routes/api/completion/+server.ts @@ -9,19 +9,13 @@ export async function POST(params: { request: Request }) { const completion_manager = new ChatbotCompletion(import.meta.env.VITE_OPENAI_API_KEY!, { openai_model: import.meta.env.VITE_OPENAI_MODEL_NAME! }); - + + await completion_manager.generate_executor(); const output = await completion_manager.query(chatHistory, input); - console.log('-------') - console.log(output); - console.log("^^^^^") - const response = new Response(output); - console.log(new Response("BRUH")); - console.log("^^^^^") return new Response(JSON.stringify(output), { - headers: { - 'Content-Type': 'text/plain' - } - }); - return new Response(); + headers: { + 'Content-Type': 'text/plain' + } + }); } From f06c2d8a1beb23c3788e043635a9fb5805958045 Mon Sep 17 00:00:00 2001 From: charliehuang09 Date: Thu, 28 Dec 2023 16:53:46 -0800 Subject: [PATCH 004/107] lint Signed-off-by: charliehuang09 --- src/lib/completion.ts | 125 ++++++++++++++++++++-------------------- src/routes/+page.svelte | 2 +- 2 files changed, 62 insertions(+), 65 deletions(-) diff --git a/src/lib/completion.ts b/src/lib/completion.ts index a8e3768..a14d227 100644 --- a/src/lib/completion.ts +++ b/src/lib/completion.ts @@ -47,8 +47,7 @@ export class ChatbotCompletion { openai_model?: string; qdrant_collection?: string; } - ) - { + ) { this.model = new ChatOpenAI({ openAIApiKey: openai_api_key, temperature: 0.7, @@ -68,17 +67,17 @@ export class ChatbotCompletion { this.tools = [ new SerpAPI(process.env.SERPAPI_API_KEY, { - location: "Austin,Texas,United States", - hl: "en", - gl: "us", + location: 'Austin,Texas,United States', + hl: 'en', + gl: 'us' }), - new Calculator(), - ]; + new Calculator() + ]; this.qdrant_collection = qdrant_collection; } - public async generate_executor(){ + public async generate_executor() { this.executor = await initializeAgentExecutorWithOptions(this.tools, this.model, { agentType: 'zero-shot-react-description', verbose: false @@ -100,76 +99,75 @@ export class ChatbotCompletion { const SUFFIX = `Begin! Question: {input} Thought:`; - console.log('checkpoint') - if (!("input" in values) || !("intermediate_steps" in values)) { - throw new Error("Missing input or agent_scratchpad from values."); - } - /** Extract and case the intermediateSteps from values as Array or an empty array if none are passed */ - const intermediateSteps = values.intermediate_steps + console.log('checkpoint'); + if (!('input' in values) || !('intermediate_steps' in values)) { + throw new Error('Missing input or agent_scratchpad from values.'); + } + /** Extract and case the intermediateSteps from values as Array or an empty array if none are passed */ + const intermediateSteps = values.intermediate_steps ? (values.intermediate_steps as Array) : []; - /** Call the helper `formatLogToString` which returns the steps as a string */ - const agentScratchpad = formatLogToString(intermediateSteps); - /** Construct the tool strings */ - const toolStrings = this.tools + /** Call the helper `formatLogToString` which returns the steps as a string */ + const agentScratchpad = formatLogToString(intermediateSteps); + /** Construct the tool strings */ + const toolStrings = this.tools .map((tool: any) => `${tool.name}: ${tool.description}`) - .join("\n"); - const toolNames = this.tools.map((tool: any) => tool.name).join(",\n"); - /** Create templates and format the instructions and suffix prompts */ - const prefixTemplate = new PromptTemplate({ + .join('\n'); + const toolNames = this.tools.map((tool: any) => tool.name).join(',\n'); + /** Create templates and format the instructions and suffix prompts */ + const prefixTemplate = new PromptTemplate({ template: PREFIX, - inputVariables: ["tools"], - }); - const instructionsTemplate = new PromptTemplate({ + inputVariables: ['tools'] + }); + const instructionsTemplate = new PromptTemplate({ template: TOOL_INSTRUCTIONS_TEMPLATE, - inputVariables: ["tool_names"], - }); - const suffixTemplate = new PromptTemplate({ + inputVariables: ['tool_names'] + }); + const suffixTemplate = new PromptTemplate({ template: SUFFIX, - inputVariables: ["input"], - }); - /** Format both templates by passing in the input variables */ - const formattedPrefix = await prefixTemplate.format({ - tools: toolStrings, - }); - const formattedInstructions = await instructionsTemplate.format({ - tool_names: toolNames, - }); - const formattedSuffix = await suffixTemplate.format({ - input: values.input, - }); - /** Construct the final prompt string */ - const formatted = [ + inputVariables: ['input'] + }); + /** Format both templates by passing in the input variables */ + const formattedPrefix = await prefixTemplate.format({ + tools: toolStrings + }); + const formattedInstructions = await instructionsTemplate.format({ + tool_names: toolNames + }); + const formattedSuffix = await suffixTemplate.format({ + input: values.input + }); + /** Construct the final prompt string */ + const formatted = [ formattedPrefix, formattedInstructions, formattedSuffix, - agentScratchpad, - ].join("\n"); - /** Return the message as a HumanMessage. */ - return [new HumanMessage(formatted)]; + agentScratchpad + ].join('\n'); + /** Return the message as a HumanMessage. */ + return [new HumanMessage(formatted)]; } private customOutputParser(text: string): AgentAction | AgentFinish { //From https://js.langchain.com/docs/modules/agents/how_to/custom_llm_agent /** If the input includes "Final Answer" return as an instance of `AgentFinish` */ - if (text.includes("Final Answer:")) { - const parts = text.split("Final Answer:"); - const input = parts[parts.length - 1].trim(); - const finalAnswers = { output: input }; - return { log: text, returnValues: finalAnswers }; + if (text.includes('Final Answer:')) { + const parts = text.split('Final Answer:'); + const input = parts[parts.length - 1].trim(); + const finalAnswers = { output: input }; + return { log: text, returnValues: finalAnswers }; } /** Use regex to extract any actions and their values */ const match = /Action: (.*)\nAction Input: (.*)/s.exec(text); if (!match) { - throw new Error(`Could not parse LLM output: ${text}`); + throw new Error(`Could not parse LLM output: ${text}`); } /** Return as an instance of `AgentAction` */ return { - tool: match[1].trim(), - toolInput: match[2].trim().replace(/^"+|"+$/g, ""), - log: text, + tool: match[1].trim(), + toolInput: match[2].trim().replace(/^"+|"+$/g, ''), + log: text }; - } - + } public async generate_executor() { this.executor = await initializeAgentExecutorWithOptions(this.tools, this.model, { @@ -177,8 +175,8 @@ export class ChatbotCompletion { verbose: false }); } - private formatMessages = async (values:InputValues) => { - // private async formatMessages(values: InputValues): Promise> { + private formatMessages = async (values: InputValues) => { + // private async formatMessages(values: InputValues): Promise> { //From https://js.langchain.com/docs/modules/agents/how_to/custom_llm_agent const PREFIX = `Answer the following questions as best you can. You have access to the following tools: {tools}.`; const TOOL_INSTRUCTIONS_TEMPLATE = `You can use the following format in your response: @@ -237,15 +235,15 @@ export class ChatbotCompletion { formattedSuffix, agentScratchpad ].join('\n'); - console.log(values) - console.log(formatted) + console.log(values); + console.log(formatted); return [new HumanMessage(formatted)]; - } + }; private customOutputParser(text: AIMessageChunk): AgentAction | AgentFinish { //From https://js.langchain.com/docs/modules/agents/how_to/custom_llm_agent /** If the input includes "Final Answer" return as an instance of `AgentFinish` */ if (text.lc_kwargs.content.includes('Final Answer:')) { - console.log(text.lc_kwargs.content) + console.log(text.lc_kwargs.content); const parts = text.lc_kwargs.content.split('Final Answer:'); const input = parts[parts.length - 1].trim(); const finalAnswers = { output: input }; @@ -280,7 +278,6 @@ export class ChatbotCompletion { tools: this.tools }); - const tmp = input; console.log(`Executing with input "${tmp}"...`); diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 79be914..dcc2dcf 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -57,7 +57,7 @@ } ]; const data = await response.json(); - console.log(data) + console.log(data); let message = data.output; history[history.length - 1].content = message; From d0a1f606cc121a21a231b52eba840804391bacfc Mon Sep 17 00:00:00 2001 From: charliehuang09 Date: Thu, 28 Dec 2023 17:26:51 -0800 Subject: [PATCH 005/107] lint#2 Signed-off-by: charliehuang09 --- src/lib/completion.ts | 113 ++------------------------- src/routes/+page.svelte | 21 +---- src/routes/api/completion/+server.ts | 1 - 3 files changed, 9 insertions(+), 126 deletions(-) diff --git a/src/lib/completion.ts b/src/lib/completion.ts index a14d227..144a9bd 100644 --- a/src/lib/completion.ts +++ b/src/lib/completion.ts @@ -1,21 +1,15 @@ -import { Document } from 'langchain/document'; - import { ChatOpenAI } from 'langchain/chat_models/openai'; import { OpenAIEmbeddings } from 'langchain/embeddings/openai'; import { type AgentAction, type AgentFinish, - SystemMessage, - BaseMessage, - AIMessage, HumanMessage, type InputValues, type AgentStep, AIMessageChunk } from 'langchain/schema'; -import { ChatHistoryType, type ChatHistory } from '$lib/history'; -import { BytesOutputParser } from 'langchain/schema/output_parser'; +import { type ChatHistory } from '$lib/history'; import { QdrantClient } from '@qdrant/js-client-rest'; import { initializeAgentExecutorWithOptions } from 'langchain/agents'; @@ -33,10 +27,7 @@ export class ChatbotCompletion { private model: ChatOpenAI; private embeddings_model: OpenAIEmbeddings; - private qdrant_client: QdrantClient; - private qdrant_collection: string; - private tools: any; - private executor: any; + private tools: object; constructor( openai_api_key: string, @@ -73,102 +64,11 @@ export class ChatbotCompletion { }), new Calculator() ]; + console.log(typeof this.tools); this.qdrant_collection = qdrant_collection; } - public async generate_executor() { - this.executor = await initializeAgentExecutorWithOptions(this.tools, this.model, { - agentType: 'zero-shot-react-description', - verbose: false - }); - } - - public async formatMessages(values: InputValues): Promise> { - //From https://js.langchain.com/docs/modules/agents/how_to/custom_llm_agent - const PREFIX = `Answer the following questions as best you can. You have access to the following tools: {tools}`; - const TOOL_INSTRUCTIONS_TEMPLATE = `Use the following format in your response: - Question: the input question you must answer - Thought: you should always think about what to do - Action: the action to take, should be one of [{tool_names}] - Action Input: the input to the action - Observation: the result of the action - ... (this Thought/Action/Action Input/Observation can repeat N times) - Thought: I now know the final answer - Final Answer: the final answer to the original input question`; - const SUFFIX = `Begin! - Question: {input} - Thought:`; - console.log('checkpoint'); - if (!('input' in values) || !('intermediate_steps' in values)) { - throw new Error('Missing input or agent_scratchpad from values.'); - } - /** Extract and case the intermediateSteps from values as Array or an empty array if none are passed */ - const intermediateSteps = values.intermediate_steps - ? (values.intermediate_steps as Array) - : []; - /** Call the helper `formatLogToString` which returns the steps as a string */ - const agentScratchpad = formatLogToString(intermediateSteps); - /** Construct the tool strings */ - const toolStrings = this.tools - .map((tool: any) => `${tool.name}: ${tool.description}`) - .join('\n'); - const toolNames = this.tools.map((tool: any) => tool.name).join(',\n'); - /** Create templates and format the instructions and suffix prompts */ - const prefixTemplate = new PromptTemplate({ - template: PREFIX, - inputVariables: ['tools'] - }); - const instructionsTemplate = new PromptTemplate({ - template: TOOL_INSTRUCTIONS_TEMPLATE, - inputVariables: ['tool_names'] - }); - const suffixTemplate = new PromptTemplate({ - template: SUFFIX, - inputVariables: ['input'] - }); - /** Format both templates by passing in the input variables */ - const formattedPrefix = await prefixTemplate.format({ - tools: toolStrings - }); - const formattedInstructions = await instructionsTemplate.format({ - tool_names: toolNames - }); - const formattedSuffix = await suffixTemplate.format({ - input: values.input - }); - /** Construct the final prompt string */ - const formatted = [ - formattedPrefix, - formattedInstructions, - formattedSuffix, - agentScratchpad - ].join('\n'); - /** Return the message as a HumanMessage. */ - return [new HumanMessage(formatted)]; - } - private customOutputParser(text: string): AgentAction | AgentFinish { - //From https://js.langchain.com/docs/modules/agents/how_to/custom_llm_agent - /** If the input includes "Final Answer" return as an instance of `AgentFinish` */ - if (text.includes('Final Answer:')) { - const parts = text.split('Final Answer:'); - const input = parts[parts.length - 1].trim(); - const finalAnswers = { output: input }; - return { log: text, returnValues: finalAnswers }; - } - /** Use regex to extract any actions and their values */ - const match = /Action: (.*)\nAction Input: (.*)/s.exec(text); - if (!match) { - throw new Error(`Could not parse LLM output: ${text}`); - } - /** Return as an instance of `AgentAction` */ - return { - tool: match[1].trim(), - toolInput: match[2].trim().replace(/^"+|"+$/g, ''), - log: text - }; - } - public async generate_executor() { this.executor = await initializeAgentExecutorWithOptions(this.tools, this.model, { agentType: 'zero-shot-react-description', @@ -202,9 +102,9 @@ export class ChatbotCompletion { const agentScratchpad = formatLogToString(intermediateSteps); /** Construct the tool strings */ const toolStrings = this.tools - .map((tool: any) => `${tool.name}: ${tool.description}`) + .map((tool: object) => `${tool.name}: ${tool.description}`) .join('\n'); - const toolNames = this.tools.map((tool: any) => tool.name).join(',\n'); + const toolNames = this.tools.map((tool: object) => tool.name).join(',\n'); /** Create templates and format the instructions and suffix prompts */ const prefixTemplate = new PromptTemplate({ template: PREFIX, @@ -235,15 +135,12 @@ export class ChatbotCompletion { formattedSuffix, agentScratchpad ].join('\n'); - console.log(values); - console.log(formatted); return [new HumanMessage(formatted)]; }; private customOutputParser(text: AIMessageChunk): AgentAction | AgentFinish { //From https://js.langchain.com/docs/modules/agents/how_to/custom_llm_agent /** If the input includes "Final Answer" return as an instance of `AgentFinish` */ if (text.lc_kwargs.content.includes('Final Answer:')) { - console.log(text.lc_kwargs.content); const parts = text.lc_kwargs.content.split('Final Answer:'); const input = parts[parts.length - 1].trim(); const finalAnswers = { output: input }; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index dcc2dcf..b91f9dc 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -2,12 +2,11 @@ import Message from './Message.svelte'; import { ChatHistoryType } from '$lib/history'; - import { streamAsyncIterator } from '$lib/iterable_stream'; import type { ChatHistory } from '$lib/history'; import { CompletionState } from '$lib/state'; import { Icon } from '@steeze-ui/svelte-icon'; - import { PaperAirplane, Trash, Stop } from '@steeze-ui/heroicons'; + import { PaperAirplane, Trash } from '@steeze-ui/heroicons'; import { writable } from 'svelte/store'; import { setContext } from 'svelte'; @@ -16,8 +15,6 @@ let userInput = ''; - let cancelStream = false; - let completionState = writable(CompletionState.Completed); setContext('completionState', completionState); @@ -65,10 +62,6 @@ completionState.set(CompletionState.Completed); } - const stop = async () => { - cancelStream = true; - }; - const reset_chat = async () => { history = []; }; @@ -86,15 +79,9 @@ placeholder="Enter your message..." class="m-auto outline-none active:border-none rounded-md p-2 flex-1" /> - {#if $completionState == CompletionState.Loading} - - {:else} - - {/if} +