From c9cac2a4d34343c95e84377fee05a049c7a72167 Mon Sep 17 00:00:00 2001 From: nerfZael Date: Wed, 30 Aug 2023 17:34:44 +0200 Subject: [PATCH 1/3] implemented agent chat message --- packages/core/src/agents/agent-function.ts | 49 +++++++------------ .../evo/agent-functions/createScript.ts | 22 ++++++++- .../evo/agent-functions/executeScript.ts | 28 +++++++++-- .../agents/evo/agent-functions/findScript.ts | 23 ++++++++- .../src/agents/evo/agent-functions/readVar.ts | 23 ++++++++- packages/core/src/agents/evo/loop.ts | 2 +- .../script-writer/agent-functions/think.ts | 25 +++++++++- .../agent-functions/writeFunction.ts | 25 +++++++++- .../core/src/agents/script-writer/loop.ts | 2 +- 9 files changed, 156 insertions(+), 43 deletions(-) diff --git a/packages/core/src/agents/agent-function.ts b/packages/core/src/agents/agent-function.ts index fd855d38..ddca211a 100644 --- a/packages/core/src/agents/agent-function.ts +++ b/packages/core/src/agents/agent-function.ts @@ -4,10 +4,6 @@ import { UNDEFINED_FUNCTION_ARGS, UNDEFINED_FUNCTION_NAME, UNPARSABLE_FUNCTION_ARGS, - FUNCTION_CALL_FAILED, - EXECUTE_SCRIPT_OUTPUT, - OTHER_EXECUTE_FUNCTION_OUTPUT, - READ_GLOBAL_VAR_OUTPUT } from "./prompts"; import { Workspace, Logger } from "../sys"; import { Scripts } from "../Scripts"; @@ -25,11 +21,20 @@ export interface AgentContext { logger: Logger; } +export type AgentFunctionResult = Result; + +export interface AgentChatMessage { + type: "success" | "error" | "info" | "warning", + title: string, + content: string, +} + export interface AgentFunction { definition: any; - buildExecutor: ( + buildChatMessage(args: any, result: AgentFunctionResult): AgentChatMessage; + buildExecutor( context: AgentContext - ) => (options: any) => Promise; + ): (options: any) => Promise; } export type ExecuteAgentFunction = ( @@ -37,43 +42,27 @@ export type ExecuteAgentFunction = ( args: string | undefined, context: AgentContext, agentFunctions: AgentFunction[], -) => Promise>; +) => Promise>; export const executeAgentFunction: ExecuteAgentFunction = async ( name: string | undefined, args: string | undefined, context: AgentContext, agentFunctions: AgentFunction[], -): Promise> => { - const result = processFunctionAndArgs(name, args, agentFunctions); +): Promise> => { + const parseResult = processFunctionAndArgs(name, args, agentFunctions); - if (!result.ok) { - return ResultErr(result.error); + if (!parseResult.ok) { + return ResultErr(parseResult.error); } - const [fnArgs, func] = result.value; - const fnName = name as string; - - const argsStr = JSON.stringify(fnArgs, null, 2); - let functionCallSummary = `# Function Call:\n\`\`\`javascript\n${fnName}(${argsStr})\n\`\`\`\n`; + const [fnArgs, func] = parseResult.value; const executor = func.buildExecutor(context); - const response = await executor(fnArgs); - - if (!response.ok) { - return ResultErr(FUNCTION_CALL_FAILED(fnName, response.error, args)); - } - - if (fnName === "executeScript") { - functionCallSummary += EXECUTE_SCRIPT_OUTPUT(fnArgs.result, response.result); - } else if (fnName === "readVar") { - functionCallSummary += READ_GLOBAL_VAR_OUTPUT(fnArgs.name, response.result); - } else { - functionCallSummary += OTHER_EXECUTE_FUNCTION_OUTPUT(response.result); - } + const result = await executor(fnArgs); - return ResultOk(functionCallSummary); + return ResultOk(func.buildChatMessage(fnArgs, result)); } function processFunctionAndArgs( diff --git a/packages/core/src/agents/evo/agent-functions/createScript.ts b/packages/core/src/agents/evo/agent-functions/createScript.ts index 9f08c27a..dc7328aa 100644 --- a/packages/core/src/agents/evo/agent-functions/createScript.ts +++ b/packages/core/src/agents/evo/agent-functions/createScript.ts @@ -1,5 +1,8 @@ import { ScriptWriter } from "../../script-writer"; -import { AgentContext, AgentFunction } from "../../agent-function"; +import { AgentChatMessage, AgentContext, AgentFunction, AgentFunctionResult } from "../../agent-function"; +import { FUNCTION_CALL_FAILED, OTHER_EXECUTE_FUNCTION_OUTPUT } from "../../prompts"; + +const FN_NAME = "createScript"; export function createScript(createScriptWriter: () => ScriptWriter): AgentFunction { return { @@ -26,6 +29,23 @@ export function createScript(createScriptWriter: () => ScriptWriter): AgentFunct additionalProperties: false }, }, + buildChatMessage(args: any, result: AgentFunctionResult): AgentChatMessage { + const argsStr = JSON.stringify(args, null, 2); + + return result.ok + ? { + type: "success", + title: `Script ${args.namespace} executed successfully!`, + content: + `# Function Call:\n\`\`\`javascript\n${FN_NAME}(${argsStr})\n\`\`\`\n` + + OTHER_EXECUTE_FUNCTION_OUTPUT(result.value), + } + : { + type: "error", + title: `Script ${args.namespace} failed to execute!`, + content: FUNCTION_CALL_FAILED(FN_NAME, result.error, args), + }; + }, buildExecutor: ( context: AgentContext ) => { diff --git a/packages/core/src/agents/evo/agent-functions/executeScript.ts b/packages/core/src/agents/evo/agent-functions/executeScript.ts index d55176b3..5f363020 100644 --- a/packages/core/src/agents/evo/agent-functions/executeScript.ts +++ b/packages/core/src/agents/evo/agent-functions/executeScript.ts @@ -1,14 +1,17 @@ -import { AgentFunction, AgentContext } from "../../agent-function"; +import { AgentFunction, AgentContext, AgentFunctionResult, AgentChatMessage } from "../../agent-function"; import { JsEngine_GlobalVar, JsEngine_Module, shimCode } from "../../../wrap"; import JSON5 from "json5"; +import { EXECUTE_SCRIPT_OUTPUT, FUNCTION_CALL_FAILED } from "../../prompts"; + +const FN_NAME = "executeScript"; export const executeScript: AgentFunction = { definition: { - name: "executeScript", + name: FN_NAME, description: `Execute an script.`, parameters: { type: "object", @@ -30,9 +33,26 @@ export const executeScript: AgentFunction = { additionalProperties: false }, }, - buildExecutor: ( + buildChatMessage(args: any, result: AgentFunctionResult): AgentChatMessage { + const argsStr = JSON.stringify(args, null, 2); + + return result.ok + ? { + type: "success", + title: `Script ${args.namespace} executed successfully!`, + content: + `# Function Call:\n\`\`\`javascript\n${FN_NAME}(${argsStr})\n\`\`\`\n` + + EXECUTE_SCRIPT_OUTPUT(args.result, result.value), + } + : { + type: "error", + title: `Script ${args.namespace} failed to execute!`, + content: FUNCTION_CALL_FAILED(FN_NAME, result.error, args), + }; + }, + buildExecutor( context: AgentContext - ) => { + ) { return async (options: { namespace: string, arguments: any, result: string }) => { try { const script = context.scripts.getScriptByName(options.namespace); diff --git a/packages/core/src/agents/evo/agent-functions/findScript.ts b/packages/core/src/agents/evo/agent-functions/findScript.ts index d0ade05f..ee2c14db 100644 --- a/packages/core/src/agents/evo/agent-functions/findScript.ts +++ b/packages/core/src/agents/evo/agent-functions/findScript.ts @@ -1,4 +1,8 @@ -import { AgentFunction, AgentContext } from "../../agent-function"; +import { ResultOk } from "@polywrap/result"; +import { AgentFunction, AgentContext, AgentFunctionResult, AgentChatMessage } from "../../agent-function"; +import { FUNCTION_CALL_FAILED, OTHER_EXECUTE_FUNCTION_OUTPUT } from "../../prompts"; + +const FN_NAME = "findScript"; export const findScript: AgentFunction = { definition: { @@ -20,6 +24,23 @@ export const findScript: AgentFunction = { additionalProperties: false }, }, + buildChatMessage(args: any, result: AgentFunctionResult): AgentChatMessage { + const argsStr = JSON.stringify(args, null, 2); + + return result.ok + ? { + type: "success", + title: `Script ${args.namespace} executed successfully!`, + content: + `# Function Call:\n\`\`\`javascript\n${FN_NAME}(${argsStr})\n\`\`\`\n` + + OTHER_EXECUTE_FUNCTION_OUTPUT(result.value), + } + : { + type: "error", + title: `Script ${args.namespace} failed to execute!`, + content: FUNCTION_CALL_FAILED(FN_NAME, result.error, args), + }; + }, buildExecutor: ( context: AgentContext ) => { diff --git a/packages/core/src/agents/evo/agent-functions/readVar.ts b/packages/core/src/agents/evo/agent-functions/readVar.ts index a0fba188..a7befa71 100644 --- a/packages/core/src/agents/evo/agent-functions/readVar.ts +++ b/packages/core/src/agents/evo/agent-functions/readVar.ts @@ -1,4 +1,8 @@ -import { AgentFunction, AgentContext } from "../../agent-function"; +import { ResultErr, ResultOk } from "@polywrap/result"; +import { AgentFunction, AgentContext, AgentFunctionResult, AgentChatMessage } from "../../agent-function"; +import { FUNCTION_CALL_FAILED, READ_GLOBAL_VAR_OUTPUT } from "../../prompts"; + +const FN_NAME = "readVar"; export const readVar: AgentFunction = { definition: { @@ -16,6 +20,23 @@ export const readVar: AgentFunction = { additionalProperties: false }, }, + buildChatMessage(args: any, result: AgentFunctionResult): AgentChatMessage { + const argsStr = JSON.stringify(args, null, 2); + + return result.ok + ? { + type: "success", + title: `Script ${args.namespace} executed successfully!`, + content: + `# Function Call:\n\`\`\`javascript\n${FN_NAME}(${argsStr})\n\`\`\`\n` + + READ_GLOBAL_VAR_OUTPUT(args.name, result.value), + } + : { + type: "error", + title: `Script ${args.namespace} failed to execute!`, + content: FUNCTION_CALL_FAILED(FN_NAME, result.error, args), + }; + }, buildExecutor: ( context: AgentContext ) => { diff --git a/packages/core/src/agents/evo/loop.ts b/packages/core/src/agents/evo/loop.ts index cad15bbb..53ba6f46 100644 --- a/packages/core/src/agents/evo/loop.ts +++ b/packages/core/src/agents/evo/loop.ts @@ -47,7 +47,7 @@ export async function* loop( ); if (result.ok) { - yield StepOutput.message(chat.temporary({ role: "system", name, content: result.value})); + yield StepOutput.message(chat.temporary({ role: "system", name, content: result.value.content})); } else { yield StepOutput.message(chat.temporary("system", result.error as string)); diff --git a/packages/core/src/agents/script-writer/agent-functions/think.ts b/packages/core/src/agents/script-writer/agent-functions/think.ts index dcedc5e2..4541369a 100644 --- a/packages/core/src/agents/script-writer/agent-functions/think.ts +++ b/packages/core/src/agents/script-writer/agent-functions/think.ts @@ -1,8 +1,12 @@ -import { AgentFunction, AgentContext } from "../../agent-function"; +import { ResultOk } from "@polywrap/result"; +import { AgentFunction, AgentContext, AgentFunctionResult, AgentChatMessage } from "../../agent-function"; +import { FUNCTION_CALL_FAILED, OTHER_EXECUTE_FUNCTION_OUTPUT } from "../../prompts"; + +const FN_NAME = "think"; export const think: AgentFunction = { definition: { - name: "think", + name: FN_NAME, description: `Think.`, parameters: { type: "object", @@ -16,6 +20,23 @@ export const think: AgentFunction = { additionalProperties: false }, }, + buildChatMessage(args: any, result: AgentFunctionResult): AgentChatMessage { + const argsStr = JSON.stringify(args, null, 2); + + return result.ok + ? { + type: "success", + title: `Script ${args.namespace} executed successfully!`, + content: + `# Function Call:\n\`\`\`javascript\n${FN_NAME}(${argsStr})\n\`\`\`\n` + + OTHER_EXECUTE_FUNCTION_OUTPUT(result.value), + } + : { + type: "error", + title: `Script ${args.namespace} failed to execute!`, + content: FUNCTION_CALL_FAILED(FN_NAME, result.error, args), + }; + }, buildExecutor: ( context: AgentContext ) => { diff --git a/packages/core/src/agents/script-writer/agent-functions/writeFunction.ts b/packages/core/src/agents/script-writer/agent-functions/writeFunction.ts index c2f9d3fa..59ceaaa5 100644 --- a/packages/core/src/agents/script-writer/agent-functions/writeFunction.ts +++ b/packages/core/src/agents/script-writer/agent-functions/writeFunction.ts @@ -1,8 +1,12 @@ -import { AgentFunction, AgentContext } from "../../agent-function"; +import { ResultErr, ResultOk } from "@polywrap/result"; +import { AgentFunction, AgentContext, AgentFunctionResult, AgentChatMessage } from "../../agent-function"; +import { FUNCTION_CALL_FAILED, OTHER_EXECUTE_FUNCTION_OUTPUT } from "../../prompts"; + +const FN_NAME = "writeFunction"; export const writeFunction: AgentFunction = { definition: { - name: "writeFunction", + name: FN_NAME, description: `Writes the function.`, parameters: { type: "object", @@ -28,6 +32,23 @@ export const writeFunction: AgentFunction = { additionalProperties: false }, }, + buildChatMessage(args: any, result: AgentFunctionResult): AgentChatMessage { + const argsStr = JSON.stringify(args, null, 2); + + return result.ok + ? { + type: "success", + title: `Function ${args.namespace} executed successfully!`, + content: + `# Function Call:\n\`\`\`javascript\n${FN_NAME}(${argsStr})\n\`\`\`\n` + + OTHER_EXECUTE_FUNCTION_OUTPUT(result.value), + } + : { + type: "error", + title: `Function ${args.namespace} failed to execute!`, + content: FUNCTION_CALL_FAILED(FN_NAME, result.error, args), + }; + }, buildExecutor: ( context: AgentContext ) => { diff --git a/packages/core/src/agents/script-writer/loop.ts b/packages/core/src/agents/script-writer/loop.ts index 896d6e34..e16a5c1d 100644 --- a/packages/core/src/agents/script-writer/loop.ts +++ b/packages/core/src/agents/script-writer/loop.ts @@ -49,7 +49,7 @@ export async function* loop( ); if (result.ok) { - yield StepOutput.message(chat.temporary({ role: "system", name, content: result.value})); + yield StepOutput.message(chat.temporary({ role: "system", name, content: result.value.content})); } else { yield StepOutput.message(chat.temporary("system", result.error as string)); From 6fc0417b5d00bc11f09dba9b7688037e1abdac47 Mon Sep 17 00:00:00 2001 From: nerfZael Date: Wed, 30 Aug 2023 17:38:08 +0200 Subject: [PATCH 2/3] merged with remote --- .../evo/agent-functions/createScript.ts | 16 ++++----- .../evo/agent-functions/executeScript.ts | 35 ++++--------------- .../agents/evo/agent-functions/findScript.ts | 17 ++++----- .../src/agents/evo/agent-functions/readVar.ts | 12 ++----- .../script-writer/agent-functions/think.ts | 7 ++-- .../agent-functions/writeFunction.ts | 14 +++----- 6 files changed, 30 insertions(+), 71 deletions(-) diff --git a/packages/core/src/agents/evo/agent-functions/createScript.ts b/packages/core/src/agents/evo/agent-functions/createScript.ts index dc7328aa..3c92e888 100644 --- a/packages/core/src/agents/evo/agent-functions/createScript.ts +++ b/packages/core/src/agents/evo/agent-functions/createScript.ts @@ -49,12 +49,9 @@ export function createScript(createScriptWriter: () => ScriptWriter): AgentFunct buildExecutor: ( context: AgentContext ) => { - return async (options: { namespace: string, description: string, arguments: string }) => { + return async (options: { namespace: string, description: string, arguments: string }): Promise> => { if (options.namespace.startsWith("agent.")) { - return { - ok: false, - result: `Cannot create an script with namespace ${options.namespace}. Try searching for script in that namespace instead.`, - } + return ResultErr(`Cannot create an script with namespace ${options.namespace}. Try searching for script in that namespace instead.`); } // Create a fresh ScriptWriter agent @@ -90,13 +87,12 @@ export function createScript(createScriptWriter: () => ScriptWriter): AgentFunct op ]; - return { - ok: true, - result: `Created the following scripts:` + + return ResultOk( + `Created the following scripts:` + `\n--------------\n` + `${candidates.map((c) => `Namespace: ${c.name}\nArguments: ${c.arguments}\nDescription: ${c.description}`).join("\n--------------\n")}` + - `\n--------------\n`, - }; + `\n--------------\n` + ); }; } }; diff --git a/packages/core/src/agents/evo/agent-functions/executeScript.ts b/packages/core/src/agents/evo/agent-functions/executeScript.ts index 5f363020..60d3b398 100644 --- a/packages/core/src/agents/evo/agent-functions/executeScript.ts +++ b/packages/core/src/agents/evo/agent-functions/executeScript.ts @@ -58,10 +58,7 @@ export const executeScript: AgentFunction = { const script = context.scripts.getScriptByName(options.namespace); if (!script) { - return { - ok: false, - error: `Script ${options.namespace} not found.`, - }; + return ResultErr(`Script ${options.namespace} not found.`); } let args: any = undefined; @@ -88,10 +85,7 @@ export const executeScript: AgentFunction = { } } } catch { - return { - ok: false, - error: `Invalid arguments provided for script ${options.namespace}: '${options.arguments}' is not valid JSON!`, - }; + return ResultErr(`Invalid arguments provided for script ${options.namespace}: '${options.arguments}' is not valid JSON!`); } const globals: JsEngine_GlobalVar[] = @@ -115,27 +109,12 @@ export const executeScript: AgentFunction = { return result.ok ? result.value.error == null ? context.client.jsPromiseOutput.ok - ? { - ok: true, - result: JSON.stringify(context.client.jsPromiseOutput.value), - } - : { - ok: false, - error: JSON.stringify(context.client.jsPromiseOutput.error), - } - : { - ok: false, - error: result.value.error + "\nCode: " + script.code, - } - : { - ok: false, - error: result.error?.toString() ?? "", - }; + ? ResultOk(JSON.stringify(context.client.jsPromiseOutput.value)) + : ResultErr(JSON.stringify(context.client.jsPromiseOutput.error)) + : ResultErr(result.value.error) + : ResultErr(result.error?.toString() ?? ""); } catch (e: any) { - return { - ok: false, - error: e, - }; + return ResultErr(e); } }; } diff --git a/packages/core/src/agents/evo/agent-functions/findScript.ts b/packages/core/src/agents/evo/agent-functions/findScript.ts index ee2c14db..f9b5ab76 100644 --- a/packages/core/src/agents/evo/agent-functions/findScript.ts +++ b/packages/core/src/agents/evo/agent-functions/findScript.ts @@ -44,24 +44,21 @@ export const findScript: AgentFunction = { buildExecutor: ( context: AgentContext ) => { - return async (options: { namespace: string, description: string }) => { + return async (options: { namespace: string, description: string }): Promise> => { const candidates = context.scripts.searchScripts( `${options.namespace} ${options.description}` ).slice(0, 5); if (candidates.length === 0) { - return { - ok: true, - result: `Found no candidates for script ${options.namespace}. Try creating the script instead.`, - }; + return ResultOk(`Found no candidates for script ${options.namespace}. Try creating the script instead.`); } - return { - ok: true, - result: `Found the following candidates for script: ${options.namespace}:` + + + return ResultOk( + `Found the following candidates for script: ${options.namespace}:` + `\n--------------\n` + `${candidates.map((c) => `Namespace: ${c.name}\nArguments: ${c.arguments}\nDescription: ${c.description}`).join("\n--------------\n")}` + - `\n--------------\n`, - }; + `\n--------------\n` + ); }; } }; diff --git a/packages/core/src/agents/evo/agent-functions/readVar.ts b/packages/core/src/agents/evo/agent-functions/readVar.ts index a7befa71..d68c7c50 100644 --- a/packages/core/src/agents/evo/agent-functions/readVar.ts +++ b/packages/core/src/agents/evo/agent-functions/readVar.ts @@ -40,18 +40,12 @@ export const readVar: AgentFunction = { buildExecutor: ( context: AgentContext ) => { - return async (options: { name: string }) => { + return async (options: { name: string }): Promise> => { if (!context.globals[options.name]) { - return { - ok: false, - result: `Global variable {{${options.name}}} not found.`, - }; + return ResultErr(`Global variable ${options.name} not found.`); } - return { - ok: true, - result: context.globals[options.name], - }; + return ResultOk(context.globals[options.name]); }; } }; diff --git a/packages/core/src/agents/script-writer/agent-functions/think.ts b/packages/core/src/agents/script-writer/agent-functions/think.ts index 4541369a..71a706c7 100644 --- a/packages/core/src/agents/script-writer/agent-functions/think.ts +++ b/packages/core/src/agents/script-writer/agent-functions/think.ts @@ -40,11 +40,8 @@ export const think: AgentFunction = { buildExecutor: ( context: AgentContext ) => { - return async (options: { thoughts: string }) => { - return { - ok: true, - result: `I think: ${options.thoughts}.`, - }; + return async (options: { thoughts: string }): Promise> => { + return ResultOk(`I think: ${options.thoughts}.`); }; } }; diff --git a/packages/core/src/agents/script-writer/agent-functions/writeFunction.ts b/packages/core/src/agents/script-writer/agent-functions/writeFunction.ts index 59ceaaa5..d94f60b6 100644 --- a/packages/core/src/agents/script-writer/agent-functions/writeFunction.ts +++ b/packages/core/src/agents/script-writer/agent-functions/writeFunction.ts @@ -1,3 +1,5 @@ +import { Result, ResultErr, ResultOk } from "@polywrap/result"; +import { AgentFunction, AgentContext } from "../../agent-function"; import { ResultErr, ResultOk } from "@polywrap/result"; import { AgentFunction, AgentContext, AgentFunctionResult, AgentChatMessage } from "../../agent-function"; import { FUNCTION_CALL_FAILED, OTHER_EXECUTE_FUNCTION_OUTPUT } from "../../prompts"; @@ -52,20 +54,14 @@ export const writeFunction: AgentFunction = { buildExecutor: ( context: AgentContext ) => { - return async (options: { namespace: string, description: string, arguments: string, code: string }) => { + return async (options: { namespace: string, description: string, arguments: string, code: string }): Promise> => { if (options.namespace.startsWith("agent.")) { - return { - ok: false, - result: `Cannot create a function with namespace ${options.namespace}. Namespaces starting with 'agent.' are reserved.`, - } + return ResultErr(`Cannot create a function with namespace ${options.namespace}. Namespaces starting with 'agent.' are reserved.`); } context.workspace.writeFileSync("index.js", options.code); - return { - ok: true, - result: `Wrote the function ${options.namespace} to the workspace.`, - }; + return ResultOk(`Wrote the function ${options.namespace} to the workspace.`); }; } }; From d4a2e02c8c03384725c537e617aab360596ab431 Mon Sep 17 00:00:00 2001 From: nerfZael Date: Wed, 30 Aug 2023 23:30:58 +0200 Subject: [PATCH 3/3] using title and content instead of just a single string for agent messages --- apps/browser/src/components/Chat/Chat.tsx | 39 ++++++++++++++---- apps/browser/src/pages/Dojo.tsx | 2 +- apps/cli/src/cli.ts | 9 +++- packages/core/src/agents/agent-function.ts | 2 +- packages/core/src/agents/agent.ts | 41 +++++++------------ packages/core/src/agents/evo/Evo.ts | 3 +- .../evo/agent-functions/createScript.ts | 20 +++++---- .../evo/agent-functions/executeScript.ts | 11 +++-- .../agents/evo/agent-functions/findScript.ts | 10 ++--- .../src/agents/evo/agent-functions/readVar.ts | 10 ++--- packages/core/src/agents/evo/loop.ts | 27 +++++++++--- .../src/agents/script-writer/ScriptWriter.ts | 3 +- .../script-writer/agent-functions/think.ts | 10 ++--- .../agent-functions/writeFunction.ts | 14 +++---- .../core/src/agents/script-writer/loop.ts | 27 +++++++++--- 15 files changed, 138 insertions(+), 90 deletions(-) diff --git a/apps/browser/src/components/Chat/Chat.tsx b/apps/browser/src/components/Chat/Chat.tsx index 4c6d4943..f238f39b 100644 --- a/apps/browser/src/components/Chat/Chat.tsx +++ b/apps/browser/src/components/Chat/Chat.tsx @@ -8,7 +8,8 @@ import { faMarkdown } from '@fortawesome/free-brands-svg-icons'; import "./Chat.css"; export interface ChatMessage { - text: string; + title: string; + content?: string; user: string; color?: string; } @@ -29,6 +30,7 @@ const Chat: React.FC = ({ evo, onMessage, messages, goalEnded }: Chat undefined ); const [stopped, setStopped] = useState(false); + const [hoveredMsgIndex, setHoveredMsgIndex] = useState(-1); const pausedRef = useRef(paused); useEffect(() => { @@ -54,7 +56,7 @@ const Chat: React.FC = ({ evo, onMessage, messages, goalEnded }: Chat // Create a new iteration thread if (!evoItr) { - const goal = messages.filter((msg) => msg.user === "user")[0].text; + const goal = messages.filter((msg) => msg.user === "user")[0].title; setEvoItr(evo.run(goal)); return Promise.resolve(); } @@ -71,9 +73,10 @@ const Chat: React.FC = ({ evo, onMessage, messages, goalEnded }: Chat const response = await evoItr.next(); - if (response.value && response.value.message) { + if (!response.done && response.value.message) { const evoMessage = { - text: response.value.message, + title: response.value.message.title, + content: response.value.message.content, user: "evo" }; messageLog = [...messageLog, evoMessage]; @@ -97,7 +100,7 @@ const Chat: React.FC = ({ evo, onMessage, messages, goalEnded }: Chat const handleSend = async () => { onMessage({ - text: message, + title: message, user: "user" }); setSending(true); @@ -127,7 +130,7 @@ const Chat: React.FC = ({ evo, onMessage, messages, goalEnded }: Chat let exportedContent = ''; if (format === 'md') { exportedContent = messages.map((msg) => { - return `# ${msg.user.toUpperCase()}\n${msg.text}\n---\n`; + return `# ${msg.user.toUpperCase()}\n${msg.title}\n${msg.content}\n---\n`; }).join('\n'); } @@ -155,8 +158,28 @@ const Chat: React.FC = ({ evo, onMessage, messages, goalEnded }: Chat {messages.map((msg, index) => (
{msg.user.toUpperCase()}
-
- {msg.text} +
setHoveredMsgIndex(index)} + onMouseLeave={() => setHoveredMsgIndex(-1)} + > +
+ { + hoveredMsgIndex === index + ? ( + <> +
{msg.title}
+ {msg.content ?? ""} + + ) + : ( + <> +
{msg.title}
+ + ) + } +
))} diff --git a/apps/browser/src/pages/Dojo.tsx b/apps/browser/src/pages/Dojo.tsx index 3cbf4b84..452d1565 100644 --- a/apps/browser/src/pages/Dojo.tsx +++ b/apps/browser/src/pages/Dojo.tsx @@ -122,7 +122,7 @@ function Dojo() { onLog: (markdown: string, color?: string) => { onMessage({ user: "evo", - text: markdown, + title: markdown, color }); } diff --git a/apps/cli/src/cli.ts b/apps/cli/src/cli.ts index fdf85436..e4c595cc 100644 --- a/apps/cli/src/cli.ts +++ b/apps/cli/src/cli.ts @@ -122,8 +122,15 @@ export async function cli(): Promise { while(true) { const response = await iterator.next(); + if (response.done) { + if (!response.value.ok) { + logger.error(response.value.error ?? "Unknown error"); + } + break; + } + if (response.value) { - response.value.message && logger.info(response.value.message); + response.value.message && logger.info(response.value.message.title); } } } diff --git a/packages/core/src/agents/agent-function.ts b/packages/core/src/agents/agent-function.ts index ddca211a..11432c89 100644 --- a/packages/core/src/agents/agent-function.ts +++ b/packages/core/src/agents/agent-function.ts @@ -26,7 +26,7 @@ export type AgentFunctionResult = Result; export interface AgentChatMessage { type: "success" | "error" | "info" | "warning", title: string, - content: string, + content?: string, } export interface AgentFunction { diff --git a/packages/core/src/agents/agent.ts b/packages/core/src/agents/agent.ts index 407a8e94..c7181204 100644 --- a/packages/core/src/agents/agent.ts +++ b/packages/core/src/agents/agent.ts @@ -1,3 +1,6 @@ +import { Result } from "@polywrap/result"; +import { AgentChatMessage } from "./agent-function"; + export abstract class Agent { abstract run( namespace: string, @@ -6,23 +9,7 @@ export abstract class Agent { ): AsyncGenerator; } -export class RunResult { - message?: string; - isError?: boolean; - - constructor(message?: string, isError?: boolean) { - this.message = message; - this.isError = isError; - } - - static ok(msg?: string): RunResult { - return new RunResult(msg); - } - - static error(msg?: string): RunResult { - return new RunResult(msg, true); - } -} +export type RunResult = Result; export enum PromptType { None, @@ -31,23 +18,23 @@ export enum PromptType { } export class StepOutput { - message: string; + message: AgentChatMessage; promptType: PromptType; - constructor(message: string, promptType?: PromptType) { - this.message = message; - this.promptType = promptType ?? PromptType.None; + constructor(message: AgentChatMessage, promptType?: PromptType) { + this.message = message; + this.promptType = promptType ?? PromptType.None; } - static message(msg?: string): StepOutput { - return new StepOutput(msg ?? ""); + static message(msg: AgentChatMessage): StepOutput { + return new StepOutput(msg); } - static prompt(msg: string): StepOutput { - return new StepOutput(msg, PromptType.Prompt); + static prompt(msg: AgentChatMessage): StepOutput { + return new StepOutput(msg, PromptType.Prompt); } - static question(msg: string): StepOutput { - return new StepOutput(msg, PromptType.Question); + static question(msg: AgentChatMessage): StepOutput { + return new StepOutput(msg, PromptType.Question); } } diff --git a/packages/core/src/agents/evo/Evo.ts b/packages/core/src/agents/evo/Evo.ts index a784f0b7..bdb00bff 100644 --- a/packages/core/src/agents/evo/Evo.ts +++ b/packages/core/src/agents/evo/Evo.ts @@ -8,6 +8,7 @@ import { WrapClient } from "../../wrap"; import { Scripts } from "../../Scripts"; import { InMemoryWorkspace, Workspace, Logger } from "../../sys"; import { IWrapPackage } from "@polywrap/client-js"; +import { ResultErr } from "@polywrap/result"; export class Evo implements Agent { private client: WrapClient; @@ -52,7 +53,7 @@ export class Evo implements Agent { ); } catch (err) { this.logger.error(err); - return RunResult.error("Unrecoverable error encountered."); + return ResultErr("Unrecoverable error encountered."); } } } diff --git a/packages/core/src/agents/evo/agent-functions/createScript.ts b/packages/core/src/agents/evo/agent-functions/createScript.ts index 3c92e888..8426efb4 100644 --- a/packages/core/src/agents/evo/agent-functions/createScript.ts +++ b/packages/core/src/agents/evo/agent-functions/createScript.ts @@ -1,6 +1,7 @@ import { ScriptWriter } from "../../script-writer"; import { AgentChatMessage, AgentContext, AgentFunction, AgentFunctionResult } from "../../agent-function"; import { FUNCTION_CALL_FAILED, OTHER_EXECUTE_FUNCTION_OUTPUT } from "../../prompts"; +import { ResultErr, ResultOk } from "@polywrap/result"; const FN_NAME = "createScript"; @@ -35,21 +36,19 @@ export function createScript(createScriptWriter: () => ScriptWriter): AgentFunct return result.ok ? { type: "success", - title: `Script ${args.namespace} executed successfully!`, + title: `Created '${args.namespace}' script.`, content: `# Function Call:\n\`\`\`javascript\n${FN_NAME}(${argsStr})\n\`\`\`\n` + OTHER_EXECUTE_FUNCTION_OUTPUT(result.value), } : { type: "error", - title: `Script ${args.namespace} failed to execute!`, + title: `Failed to create '${args.namespace}' script!`, content: FUNCTION_CALL_FAILED(FN_NAME, result.error, args), }; }, - buildExecutor: ( - context: AgentContext - ) => { - return async (options: { namespace: string, description: string, arguments: string }): Promise> => { + buildExecutor(context: AgentContext) { + return async (options: { namespace: string, description: string, arguments: string }): Promise => { if (options.namespace.startsWith("agent.")) { return ResultErr(`Cannot create an script with namespace ${options.namespace}. Try searching for script in that namespace instead.`); } @@ -64,7 +63,14 @@ export function createScript(createScriptWriter: () => ScriptWriter): AgentFunct while(true) { const response = await iterator.next(); - response.value.message && context.logger.info(response.value.message); + if (response.done) { + if (!response.value.ok) { + return ResultErr(response.value.error); + } + break; + } + + response.value && context.logger.info(response.value.message.title); // TODO: we should not be communicating the ScriptWriter's completion // via a special file in the workspace diff --git a/packages/core/src/agents/evo/agent-functions/executeScript.ts b/packages/core/src/agents/evo/agent-functions/executeScript.ts index 60d3b398..bcb53c31 100644 --- a/packages/core/src/agents/evo/agent-functions/executeScript.ts +++ b/packages/core/src/agents/evo/agent-functions/executeScript.ts @@ -6,6 +6,7 @@ import { } from "../../../wrap"; import JSON5 from "json5"; import { EXECUTE_SCRIPT_OUTPUT, FUNCTION_CALL_FAILED } from "../../prompts"; +import { ResultErr, ResultOk } from "@polywrap/result"; const FN_NAME = "executeScript"; @@ -39,21 +40,19 @@ export const executeScript: AgentFunction = { return result.ok ? { type: "success", - title: `Script ${args.namespace} executed successfully!`, + title: `Executed '${args.namespace}' script.`, content: `# Function Call:\n\`\`\`javascript\n${FN_NAME}(${argsStr})\n\`\`\`\n` + EXECUTE_SCRIPT_OUTPUT(args.result, result.value), } : { type: "error", - title: `Script ${args.namespace} failed to execute!`, + title: `'${args.namespace}' script failed to execute!`, content: FUNCTION_CALL_FAILED(FN_NAME, result.error, args), }; }, - buildExecutor( - context: AgentContext - ) { - return async (options: { namespace: string, arguments: any, result: string }) => { + buildExecutor(context: AgentContext) { + return async (options: { namespace: string, arguments: any, result: string }): Promise => { try { const script = context.scripts.getScriptByName(options.namespace); diff --git a/packages/core/src/agents/evo/agent-functions/findScript.ts b/packages/core/src/agents/evo/agent-functions/findScript.ts index f9b5ab76..1da0f4b6 100644 --- a/packages/core/src/agents/evo/agent-functions/findScript.ts +++ b/packages/core/src/agents/evo/agent-functions/findScript.ts @@ -30,21 +30,19 @@ export const findScript: AgentFunction = { return result.ok ? { type: "success", - title: `Script ${args.namespace} executed successfully!`, + title: `Searched for '${args.namespace}' script ("${args.description}")`, content: `# Function Call:\n\`\`\`javascript\n${FN_NAME}(${argsStr})\n\`\`\`\n` + OTHER_EXECUTE_FUNCTION_OUTPUT(result.value), } : { type: "error", - title: `Script ${args.namespace} failed to execute!`, + title: `Failed to search for '${args.namespace}' script!`, content: FUNCTION_CALL_FAILED(FN_NAME, result.error, args), }; }, - buildExecutor: ( - context: AgentContext - ) => { - return async (options: { namespace: string, description: string }): Promise> => { + buildExecutor(context: AgentContext) { + return async (options: { namespace: string, description: string }): Promise => { const candidates = context.scripts.searchScripts( `${options.namespace} ${options.description}` ).slice(0, 5); diff --git a/packages/core/src/agents/evo/agent-functions/readVar.ts b/packages/core/src/agents/evo/agent-functions/readVar.ts index d68c7c50..97d9ee0f 100644 --- a/packages/core/src/agents/evo/agent-functions/readVar.ts +++ b/packages/core/src/agents/evo/agent-functions/readVar.ts @@ -26,21 +26,19 @@ export const readVar: AgentFunction = { return result.ok ? { type: "success", - title: `Script ${args.namespace} executed successfully!`, + title: `Read '${args.name}' variable.`, content: `# Function Call:\n\`\`\`javascript\n${FN_NAME}(${argsStr})\n\`\`\`\n` + READ_GLOBAL_VAR_OUTPUT(args.name, result.value), } : { type: "error", - title: `Script ${args.namespace} failed to execute!`, + title: `Failed to read ${args.namespace} variable!`, content: FUNCTION_CALL_FAILED(FN_NAME, result.error, args), }; }, - buildExecutor: ( - context: AgentContext - ) => { - return async (options: { name: string }): Promise> => { + buildExecutor(context: AgentContext) { + return async (options: { name: string }): Promise => { if (!context.globals[options.name]) { return ResultErr(`Global variable ${options.name} not found.`); } diff --git a/packages/core/src/agents/evo/loop.ts b/packages/core/src/agents/evo/loop.ts index 53ba6f46..6da4ec02 100644 --- a/packages/core/src/agents/evo/loop.ts +++ b/packages/core/src/agents/evo/loop.ts @@ -5,6 +5,7 @@ import { LlmApi, LlmResponse, Chat } from "../../llm"; import { WrapClient } from "../../wrap"; import { Scripts } from "../../Scripts"; import { Workspace, Logger } from "../../sys"; +import { ResultErr } from "@polywrap/result"; export async function* loop( goal: string, @@ -27,7 +28,7 @@ export async function* loop( const response = await llm.getResponse(chat, agentFunctions.map(f => f.definition)); if (!response) { - return RunResult.error("No response from LLM."); + return ResultErr("No response from LLM."); } if (response.function_call) { @@ -47,10 +48,17 @@ export async function* loop( ); if (result.ok) { - yield StepOutput.message(chat.temporary({ role: "system", name, content: result.value.content})); + chat.temporary({ role: "system", name, content: result.value.content}); + yield StepOutput.message(result.value); } else { - yield StepOutput.message(chat.temporary("system", result.error as string)); + chat.temporary("system", result.error as string); + + yield StepOutput.message({ + type: "error", + title: `Failed to execute ${name}!`, + content: result.error as string + }); } } else { yield* _preventLoopAndSaveMsg(chat, response); @@ -62,8 +70,17 @@ async function* _preventLoopAndSaveMsg(chat: Chat, response: LlmResponse): Async if (chat.messages[chat.messages.length - 1].content === response.content && chat.messages[chat.messages.length - 2].content === response.content) { chat.temporary("system", LOOP_PREVENTION_PROMPT); - yield StepOutput.message(LOOP_PREVENTION_PROMPT); + yield StepOutput.message({ + type: "warning", + title: "Agent is getting stuck. Recovering...", + content: LOOP_PREVENTION_PROMPT + }); } else { - yield StepOutput.message(chat.temporary(response)); + chat.temporary(response); + yield StepOutput.message({ + type: "success", + title: "Agent response", + content: response.content ?? "" + }); } } diff --git a/packages/core/src/agents/script-writer/ScriptWriter.ts b/packages/core/src/agents/script-writer/ScriptWriter.ts index 22803089..7fb29fc3 100644 --- a/packages/core/src/agents/script-writer/ScriptWriter.ts +++ b/packages/core/src/agents/script-writer/ScriptWriter.ts @@ -6,6 +6,7 @@ import { WrapClient } from "../../wrap"; import { Scripts } from "../../Scripts"; import { LlmApi, Chat } from "../../llm"; import { Workspace, Logger } from "../../sys"; +import { ResultErr } from "@polywrap/result"; export class ScriptWriter implements Agent { private client: WrapClient; @@ -48,7 +49,7 @@ export class ScriptWriter implements Agent { ); } catch (err) { this.logger.error(err); - return RunResult.error( "Unrecoverable error encountered."); + return ResultErr("Unrecoverable error encountered."); } } } diff --git a/packages/core/src/agents/script-writer/agent-functions/think.ts b/packages/core/src/agents/script-writer/agent-functions/think.ts index 71a706c7..12f3e54c 100644 --- a/packages/core/src/agents/script-writer/agent-functions/think.ts +++ b/packages/core/src/agents/script-writer/agent-functions/think.ts @@ -26,21 +26,19 @@ export const think: AgentFunction = { return result.ok ? { type: "success", - title: `Script ${args.namespace} executed successfully!`, + title: `Thinking...`, content: `# Function Call:\n\`\`\`javascript\n${FN_NAME}(${argsStr})\n\`\`\`\n` + OTHER_EXECUTE_FUNCTION_OUTPUT(result.value), } : { type: "error", - title: `Script ${args.namespace} failed to execute!`, + title: `Failed to think!`, content: FUNCTION_CALL_FAILED(FN_NAME, result.error, args), }; }, - buildExecutor: ( - context: AgentContext - ) => { - return async (options: { thoughts: string }): Promise> => { + buildExecutor(context: AgentContext) { + return async (options: { thoughts: string }): Promise => { return ResultOk(`I think: ${options.thoughts}.`); }; } diff --git a/packages/core/src/agents/script-writer/agent-functions/writeFunction.ts b/packages/core/src/agents/script-writer/agent-functions/writeFunction.ts index d94f60b6..ecebb89d 100644 --- a/packages/core/src/agents/script-writer/agent-functions/writeFunction.ts +++ b/packages/core/src/agents/script-writer/agent-functions/writeFunction.ts @@ -1,7 +1,5 @@ -import { Result, ResultErr, ResultOk } from "@polywrap/result"; -import { AgentFunction, AgentContext } from "../../agent-function"; import { ResultErr, ResultOk } from "@polywrap/result"; -import { AgentFunction, AgentContext, AgentFunctionResult, AgentChatMessage } from "../../agent-function"; +import { AgentFunction, AgentFunctionResult, AgentChatMessage, AgentContext } from "../../agent-function"; import { FUNCTION_CALL_FAILED, OTHER_EXECUTE_FUNCTION_OUTPUT } from "../../prompts"; const FN_NAME = "writeFunction"; @@ -40,21 +38,19 @@ export const writeFunction: AgentFunction = { return result.ok ? { type: "success", - title: `Function ${args.namespace} executed successfully!`, + title: `Wrote function '${args.namespace}'.`, content: `# Function Call:\n\`\`\`javascript\n${FN_NAME}(${argsStr})\n\`\`\`\n` + OTHER_EXECUTE_FUNCTION_OUTPUT(result.value), } : { type: "error", - title: `Function ${args.namespace} failed to execute!`, + title: `Failed to write function '${args.namespace}'!`, content: FUNCTION_CALL_FAILED(FN_NAME, result.error, args), }; }, - buildExecutor: ( - context: AgentContext - ) => { - return async (options: { namespace: string, description: string, arguments: string, code: string }): Promise> => { + buildExecutor(context: AgentContext) { + return async (options: { namespace: string, description: string, arguments: string, code: string }): Promise => { if (options.namespace.startsWith("agent.")) { return ResultErr(`Cannot create a function with namespace ${options.namespace}. Namespaces starting with 'agent.' are reserved.`); } diff --git a/packages/core/src/agents/script-writer/loop.ts b/packages/core/src/agents/script-writer/loop.ts index e16a5c1d..4555610d 100644 --- a/packages/core/src/agents/script-writer/loop.ts +++ b/packages/core/src/agents/script-writer/loop.ts @@ -5,6 +5,7 @@ import { LlmApi, LlmResponse, Chat } from "../../llm"; import { WrapClient } from "../../wrap"; import { Scripts } from "../../Scripts"; import { Workspace, Logger } from "../../sys"; +import { ResultErr } from "@polywrap/result"; export async function* loop( namespace: string, @@ -29,7 +30,7 @@ export async function* loop( const response = await llm.getResponse(chat, agentFunctions.map(f => f.definition)); if (!response) { - return RunResult.error("No response from LLM."); + return ResultErr("No response from LLM."); } if (response.function_call) { @@ -49,10 +50,17 @@ export async function* loop( ); if (result.ok) { - yield StepOutput.message(chat.temporary({ role: "system", name, content: result.value.content})); + chat.temporary({ role: "system", name, content: result.value.content}); + yield StepOutput.message(result.value); } else { - yield StepOutput.message(chat.temporary("system", result.error as string)); + chat.temporary("system", result.error as string); + + yield StepOutput.message({ + type: "error", + title: `Failed to execute ${name}!`, + content: result.error as string + }); } } else { yield* _preventLoopAndSaveMsg(chat, response); @@ -64,8 +72,17 @@ async function* _preventLoopAndSaveMsg(chat: Chat, response: LlmResponse): Async if (chat.messages[chat.messages.length - 1].content === response.content && chat.messages[chat.messages.length - 2].content === response.content) { chat.temporary("system", LOOP_PREVENTION_PROMPT); - yield StepOutput.message(LOOP_PREVENTION_PROMPT); + yield StepOutput.message({ + type: "warning", + title: "Loop prevention", + content: LOOP_PREVENTION_PROMPT + }); } else { - yield StepOutput.message(chat.temporary(response)); + chat.temporary(response); + yield StepOutput.message({ + type: "success", + title: "Agent response", + content: response.content ?? "" + }); } }