From 8c5c3272165e3112442b4acc529a0e052177b3cc Mon Sep 17 00:00:00 2001 From: moon Date: Tue, 13 Feb 2024 16:30:19 -0800 Subject: [PATCH] init commit --- .github/workflows/deploy_worker.yaml | 27 + .gitignore | 3 +- README.md | 6 +- package.json | 3 +- rollup.config.js | 11 +- .../cj/actions/__tests__/introduce.test.ts | 15 +- src/agents/cj/actions/index.ts | 4 +- src/agents/cj/actions/introduce.ts | 3 - .../cj/evaluators/__tests__/details.test.ts | 6 - .../cj/evaluators/__tests__/profile.test.ts | 4 +- src/agents/cj/evaluators/details.ts | 9 +- src/agents/cj/evaluators/profile.ts | 16 - src/agents/cj/index.ts | 11 +- src/agents/simple/index.ts | 4 - src/lib/__tests__/actions.test.ts | 2 - src/lib/__tests__/memory.test.ts | 2 - src/lib/__tests__/runtime.test.ts | 2 - src/lib/actions.ts | 6 +- src/lib/actions/__test__/ignore.test.ts | 3 - src/lib/evaluation.ts | 1 - src/lib/evaluators/reflect.ts | 13 +- src/lib/goals.ts | 1 - src/lib/index.ts | 14 +- src/lib/logger.ts | 648 +++++++++++++++++- src/lib/memory.ts | 1 - src/lib/messages.ts | 14 - src/lib/relationships.ts | 4 - src/lib/runtime.ts | 11 +- src/lib/templates.ts | 1 - src/lib/types.ts | 1 - src/lib/utils.ts | 20 +- src/test/createRuntime.ts | 1 - 32 files changed, 702 insertions(+), 165 deletions(-) create mode 100644 .github/workflows/deploy_worker.yaml diff --git a/.github/workflows/deploy_worker.yaml b/.github/workflows/deploy_worker.yaml new file mode 100644 index 0000000..74e292c --- /dev/null +++ b/.github/workflows/deploy_worker.yaml @@ -0,0 +1,27 @@ +name: Deploy Cloudflare Worker +on: + push: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-latest + name: Deploy + steps: + - uses: actions/checkout@v3 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install Dependencies + run: npm install + + - name: Deploy Worker + env: + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + run: npx wrangler deploy \ No newline at end of file diff --git a/.gitignore b/.gitignore index e560229..c0599b2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ node_modules .env concatenated-output.ts -embedding-cache.json \ No newline at end of file +embedding-cache.json +dist \ No newline at end of file diff --git a/README.md b/README.md index 220a5fa..074dd8e 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,16 @@ Flexible, scalable and customizable agents to do your bidding. - +![cj](https://github.com/lalalune/bgent/assets/18633264/7513b5a6-2352-45f3-8b87-7ee0e2171a30) [![License](https://img.shields.io/badge/License-MIT-blue)](https://github.com/lalalune/bgent/blob/main/LICENSE) [![stars - bgent](https://img.shields.io/github/stars/lalalune/bgent?style=social)](https://github.com/lalalune/bgent) [![forks - bgent](https://img.shields.io/github/forks/lalalune/bgent?style=social)](https://github.com/lalalune/bgent) +## PRE-ALPHA RELEASE +- This code is NOT production ready. This package has been released as-is to enable collaboration and development. +- 0.1.0 will be the first official alpha release! + ## Features - Simple and extensible diff --git a/package.json b/package.json index 9f877bd..f45c4f9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bgent", - "version": "0.0.1", + "version": "0.0.3", "private": false, "description": "bgent. because agent was taken.", "type": "module", @@ -60,7 +60,6 @@ "@rollup/plugin-json": "^6.1.0", "@supabase/supabase-js": "^2.39.3", "@tsndr/cloudflare-worker-jwt": "^2.2.1", - "chalk": "^5.3.0", "dotenv": "^16.4.4", "inquirer": "^9.2.14", "ts-node": "^10.9.2" diff --git a/rollup.config.js b/rollup.config.js index ba4653d..9ad3991 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -25,11 +25,10 @@ export default defineConfig([ ], plugins: [ json(), - resolve(), // so Rollup can resolve packages - commonjs(), // so Rollup can convert commonjs to an ES module - typescript(), // so Rollup can convert TypeScript to JavaScript + resolve(), + commonjs(), + typescript(), replace({ - // see: https://github.com/rollup/plugins/tree/master/packages/replace#preventassignment preventAssignment: true, }) ].filter(Boolean), @@ -55,11 +54,9 @@ export default defineConfig([ ], plugins: [ json(), - typescript(), // so Rollup can convert TypeScript to JavaScript + typescript(), replace({ - // preserve to be handled by bundlers __DEV__: `(process.env.NODE_ENV !== 'production')`, - // see: https://github.com/rollup/plugins/tree/master/packages/replace#preventassignment preventAssignment: true, }) ].filter(Boolean), diff --git a/src/agents/cj/actions/__tests__/introduce.test.ts b/src/agents/cj/actions/__tests__/introduce.test.ts index 9713581..8846875 100644 --- a/src/agents/cj/actions/__tests__/introduce.test.ts +++ b/src/agents/cj/actions/__tests__/introduce.test.ts @@ -1,4 +1,3 @@ -// test creating an agent runtime import dotenv from "dotenv"; import { type UUID } from "crypto"; @@ -20,7 +19,6 @@ import { } from "../../../../test/cache"; dotenv.config(); -// create a UUID of 0s const zeroUuid = "00000000-0000-0000-0000-000000000000"; describe("Introduce Action", () => { @@ -55,7 +53,6 @@ describe("Introduce Action", () => { } async function _testCreateProfile() { - // first, add all the memories for conversation let conversation = GetTellMeAboutYourselfConversation1(user?.id as UUID); for (let i = 0; i < conversation.length; i++) { const c = conversation[i]; @@ -79,8 +76,7 @@ describe("Introduce Action", () => { const handler = evaluator.handler!; - // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression - let result = (await handler(runtime, message)) as unknown as string; + let result = (await handler(runtime, message)) as string; expect(result.includes("programmer")).toBe(true); @@ -113,7 +109,6 @@ describe("Introduce Action", () => { const previousDescriptions = [jimProfileExample1, jimProfileExample2]; - // for each description in previousDescriptions, add it to the memory for (let i = 0; i < previousDescriptions.length; i++) { const c = previousDescriptions[i]; const bakedMemory = @@ -124,13 +119,11 @@ describe("Introduce Action", () => { room_id, }); await runtime.descriptionManager.createMemory(bakedMemory); - // wait for .2 seconds + await new Promise((resolve) => setTimeout(resolve, 250)); } - // TODO: fix this - // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression - result = (await handler(runtime, message)) as unknown as string; + result = (await handler(runtime, message)) as string; expect(result.includes("38")).toBe(true); @@ -141,12 +134,10 @@ describe("Introduce Action", () => { expect(result.toLowerCase().includes("startup")).toBe(true); } - // first, destroy all memories where the user_id is TestUser await _cleanup(); await _testCreateProfile(); - // then destroy all memories again await _cleanup(); }, 60000); }); diff --git a/src/agents/cj/actions/index.ts b/src/agents/cj/actions/index.ts index 13380bb..8c41d93 100644 --- a/src/agents/cj/actions/index.ts +++ b/src/agents/cj/actions/index.ts @@ -1,6 +1,4 @@ import { type Action } from "../../../lib/types"; import introduce from "./introduce"; -/** - * A list of tools/actions available to the agent - */ + export default [introduce] as Action[]; diff --git a/src/agents/cj/actions/introduce.ts b/src/agents/cj/actions/introduce.ts index fa1d84b..a212790 100644 --- a/src/agents/cj/actions/introduce.ts +++ b/src/agents/cj/actions/introduce.ts @@ -73,13 +73,11 @@ const handler = async (runtime: BgentRuntime, message: Message) => { let responseData = null; for (let triesLeft = 3; triesLeft > 0; triesLeft--) { - // generate the response const response = await runtime.completion({ context, stop: [], }); - // parse the response, which is a json object block const parsedResponse = parseJSONObjectFromText(response); if (parsedResponse) { @@ -109,7 +107,6 @@ export default { _runtime: BgentRuntime, _message: Message, ): Promise => { - // immediatel resolve true return await Promise.resolve(true); }, description: diff --git a/src/agents/cj/evaluators/__tests__/details.test.ts b/src/agents/cj/evaluators/__tests__/details.test.ts index 2da4d11..9827df5 100644 --- a/src/agents/cj/evaluators/__tests__/details.test.ts +++ b/src/agents/cj/evaluators/__tests__/details.test.ts @@ -1,4 +1,3 @@ -// test creating an agent runtime import dotenv from "dotenv"; import { type UUID } from "crypto"; @@ -18,7 +17,6 @@ import { } from "../../../../test/cache"; dotenv.config(); -// create a UUID of 0s const zeroUuid = "00000000-0000-0000-0000-000000000000"; describe("User Details", () => { @@ -54,7 +52,6 @@ describe("User Details", () => { } async function _testGetDetails() { - // first, add all the memories for conversation let conversation = GetTellMeAboutYourselfConversation1(user?.id as UUID); for (let i = 0; i < conversation.length; i++) { const c = conversation[i]; @@ -69,7 +66,6 @@ describe("User Details", () => { embedding, }); await runtime.messageManager.createMemory(bakedMemory); - // wait for .2 seconds if (!embedding) { writeCachedEmbedding(c.content, bakedMemory.embedding as number[]); await new Promise((resolve) => setTimeout(resolve, 250)); @@ -125,12 +121,10 @@ describe("User Details", () => { expect(locationIncludesSanFrancisco).toBe(true); } - // first, destroy all memories where the user_id is TestUser await _cleanup(); await _testGetDetails(); - // then destroy all memories again await _cleanup(); }, 60000); }); diff --git a/src/agents/cj/evaluators/__tests__/profile.test.ts b/src/agents/cj/evaluators/__tests__/profile.test.ts index 2d9690b..0c59869 100644 --- a/src/agents/cj/evaluators/__tests__/profile.test.ts +++ b/src/agents/cj/evaluators/__tests__/profile.test.ts @@ -1,4 +1,3 @@ -// test creating an agent runtime import dotenv from "dotenv"; import { type UUID } from "crypto"; @@ -23,7 +22,6 @@ import evaluator from "../profile"; dotenv.config(); -// create a UUID of 0s const zeroUuid: UUID = "00000000-0000-0000-0000-000000000000"; let runtime: BgentRuntime; let user: User; @@ -164,7 +162,7 @@ describe("User Profile", () => { expect(result.toLowerCase().includes("francisco")).toBe(true); - expect(result.toLowerCase().includes("startup")).toBe(true); + expect(result.toLowerCase().includes("startup") || result.toLowerCase().includes("programmer")).toBe(true); const descriptions = await runtime.descriptionManager.getMemoriesByIds({ userIds: [message.senderId, message.agentId] as UUID[], diff --git a/src/agents/cj/evaluators/details.ts b/src/agents/cj/evaluators/details.ts index a6b55e4..8e56d03 100644 --- a/src/agents/cj/evaluators/details.ts +++ b/src/agents/cj/evaluators/details.ts @@ -12,7 +12,7 @@ Using the most recent conversation, get the details for the user's name, age, lo Only include the values that can be extracted from the conversation. Then respond with a JSON object containing a field for description in a JSON block formatted for markdown with this structure: \`\`\`json -{ user: {{senderName}}, name?: string, age?: number, location?: string, gender?: string} +{ name?: string, age?: number, location?: string, gender?: string} \`\`\` Your response must include the JSON block.`; @@ -30,13 +30,11 @@ const handler = async (runtime: BgentRuntime, message: Message) => { let responseData = null; for (let triesLeft = 3; triesLeft > 0; triesLeft--) { - // generate the response const response = await runtime.completion({ context, stop: [], }); - // parse the response, which is a json object block const parsedResponse = parseJSONObjectFromText(response); if (parsedResponse) { @@ -61,7 +59,6 @@ const handler = async (runtime: BgentRuntime, message: Message) => { const { user, name, age, location, gender } = responseData; - // find the user const response = await runtime.supabase .from("accounts") .select("*") @@ -74,7 +71,6 @@ const handler = async (runtime: BgentRuntime, message: Message) => { const currentDetails = userRecord.details || {}; - // for name, age, location, gender -- if the value exists and doesn't exist in currentDetails, add it if (name && !currentDetails.name) { currentDetails.name = name; } @@ -91,7 +87,6 @@ const handler = async (runtime: BgentRuntime, message: Message) => { currentDetails.gender = gender; } - // update the user with the new details const { error: updateError } = await runtime.supabase .from("accounts") .update({ details: currentDetails }) @@ -100,7 +95,6 @@ const handler = async (runtime: BgentRuntime, message: Message) => { console.error("error updating user", updateError); } - // respond with the details return { name, age, @@ -115,7 +109,6 @@ export default { _runtime: BgentRuntime, _message: Message, ): Promise => { - // immediatel resolve true return await Promise.resolve(true); }, description: diff --git a/src/agents/cj/evaluators/profile.ts b/src/agents/cj/evaluators/profile.ts index a038b64..99db508 100644 --- a/src/agents/cj/evaluators/profile.ts +++ b/src/agents/cj/evaluators/profile.ts @@ -1,19 +1,3 @@ -// - Gather general information about the user - -// - Info we want: - -// Basics / Logistics -// - Name -// - Gender -// - Age -// - Location - -// Deeper -// - What is important to you in a connection? -// - Describe the best aspects of your best connection. -// - Describe what went well and what went poorly. -// - How much do they value interests and hobbies vs other things - import { type UUID } from "crypto"; import { getRelationship, type BgentRuntime } from "../../../lib"; import { composeContext } from "../../../lib/context"; diff --git a/src/agents/cj/index.ts b/src/agents/cj/index.ts index 8f38bfa..d66b983 100644 --- a/src/agents/cj/index.ts +++ b/src/agents/cj/index.ts @@ -3,14 +3,12 @@ import jwt from "@tsndr/cloudflare-worker-jwt"; import { type UUID } from "crypto"; import logger from "../../lib/logger"; import { BgentRuntime } from "../../lib/runtime"; -import { composeState } from "../../lib/state"; import { type Content, type Message, type State } from "../../lib/types"; import { shouldSkipMessage } from "../../lib/utils"; import actions from "./actions"; import evaluators from "./evaluators"; import flavor from "./flavor"; -// main entry point for the agent const onMessage = async ( message: Message, runtime: BgentRuntime, @@ -18,7 +16,6 @@ const onMessage = async ( ) => { const { content: senderContent, senderId, agentId } = message; - // if userIds is not defined, set it to [senderId, agentId] if (!message.userIds) { message.userIds = [senderId!, agentId!]; } @@ -48,7 +45,6 @@ class Route { path; handler; - // handler is an async function which takes HandlerArgs and returns Promise constructor({ path = /^/, handler, @@ -69,7 +65,6 @@ const routes: Route[] = [ return; } - // parse the body from the request const message = await req.json(); const runtime = new BgentRuntime({ @@ -114,7 +109,6 @@ const routes: Route[] = [ return; } - // parse the body from the request const message = (await req.json()) as Message; const runtime = new BgentRuntime({ @@ -220,7 +214,6 @@ async function handleRequest( } } - // Default handler if no other routes are called return _setHeaders( new Response( JSON.stringify({ content: "No handler found for this path" }), @@ -242,9 +235,8 @@ export const fetch = async ( ) => { try { const res = (await handleRequest(request, env)) as Response; - return _setHeaders(res); // Ensure _setHeaders modifies the response and returns it + return _setHeaders(res); } catch (error) { - // Catch any errors that occur during handling and return a Response object return new Response(JSON.stringify({ error: (error as Error).message }), { status: 500, headers: { "Content-Type": "application/json" }, @@ -289,7 +281,6 @@ function _setHeaders(res: Response) { ]; for (const { key, value } of defaultHeaders) { - // if res.headers doesnt contain, add if (!res.headers.has(key)) res.headers.append(key, value); } return res; diff --git a/src/agents/simple/index.ts b/src/agents/simple/index.ts index 2458c84..c063034 100644 --- a/src/agents/simple/index.ts +++ b/src/agents/simple/index.ts @@ -3,10 +3,8 @@ import jwt from "@tsndr/cloudflare-worker-jwt"; import { type UUID } from "crypto"; import logger from "../../lib/logger"; import { BgentRuntime } from "../../lib/runtime"; -// import { composeState } from '../../lib/state' import { type Content, type Message, type State } from "../../lib/types"; -// main entry point for the agent const onMessage = async ( message: Message, runtime: BgentRuntime, @@ -14,7 +12,6 @@ const onMessage = async ( ) => { const { content: senderContent, senderId, agentId } = message; - // if userIds is not defined, set it to [senderId, agentId] if (!message.userIds) { message.userIds = [senderId!, agentId!]; } @@ -44,7 +41,6 @@ class Route { path; handler; - // handler is an async function which takes HandlerArgs and returns Promise constructor({ path = /^/, handler, diff --git a/src/lib/__tests__/actions.test.ts b/src/lib/__tests__/actions.test.ts index 03ff6e4..27b66e5 100644 --- a/src/lib/__tests__/actions.test.ts +++ b/src/lib/__tests__/actions.test.ts @@ -5,8 +5,6 @@ import { getCachedEmbedding, writeCachedEmbedding } from "../../test/cache"; import { createRuntime } from "../../test/createRuntime"; import { getRelationship } from "../relationships"; import { type BgentRuntime } from "../runtime"; -// import { composeState } from '../state' -// import { type Message } from '../types' dotenv.config(); diff --git a/src/lib/__tests__/memory.test.ts b/src/lib/__tests__/memory.test.ts index bea17cd..d0a6691 100644 --- a/src/lib/__tests__/memory.test.ts +++ b/src/lib/__tests__/memory.test.ts @@ -1,5 +1,3 @@ -// memory.test.ts - import { type User } from "@supabase/supabase-js"; import { type UUID } from "crypto"; import dotenv from "dotenv"; diff --git a/src/lib/__tests__/runtime.test.ts b/src/lib/__tests__/runtime.test.ts index 14d476b..e2175d7 100644 --- a/src/lib/__tests__/runtime.test.ts +++ b/src/lib/__tests__/runtime.test.ts @@ -1,4 +1,3 @@ -// test creating an agent runtime import dotenv from "dotenv"; import { createRuntime } from "../../test/createRuntime"; @@ -7,7 +6,6 @@ import { getRelationship } from "../relationships"; import { getCachedEmbedding, writeCachedEmbedding } from "../../test/cache"; dotenv.config(); -// create a UUID of 0s const zeroUuid = "00000000-0000-0000-0000-000000000000"; describe("Agent Runtime", () => { diff --git a/src/lib/actions.ts b/src/lib/actions.ts index b37b088..abfaaf7 100644 --- a/src/lib/actions.ts +++ b/src/lib/actions.ts @@ -6,11 +6,9 @@ import { type Action } from "./types"; export const defaultActions: Action[] = [continue_, wait, ignore]; export function getFormattedActions(actions: Action[]) { - const formattedActions = actions.map((action) => { + return actions.map((action) => { return `${action.name} - ${action.description}`; - }); - // join into a single string - return formattedActions.join("\n"); + }).join("\n"); } export function formatActionNames(actions: Action[]) { diff --git a/src/lib/actions/__test__/ignore.test.ts b/src/lib/actions/__test__/ignore.test.ts index 1a5c75b..534841c 100644 --- a/src/lib/actions/__test__/ignore.test.ts +++ b/src/lib/actions/__test__/ignore.test.ts @@ -3,9 +3,6 @@ import { type UUID } from "crypto"; import dotenv from "dotenv"; import { getCachedEmbedding, writeCachedEmbedding } from "../../../test/cache"; import { createRuntime } from "../../../test/createRuntime"; -// import { -// GetTellMeAboutYourselfConversation1, InnapropriateConversation1, -// } from '../../../test/data' import { getRelationship } from "../../relationships"; import { type BgentRuntime } from "../../runtime"; import { type Message } from "../../types"; diff --git a/src/lib/evaluation.ts b/src/lib/evaluation.ts index ff00d44..c8ef1c7 100644 --- a/src/lib/evaluation.ts +++ b/src/lib/evaluation.ts @@ -1,7 +1,6 @@ import reflect from "./evaluators/reflect"; import { type Evaluator } from "./types"; -// evaluate runs the evaluators export const defaultEvaluators: Evaluator[] = [ reflect, // goal, diff --git a/src/lib/evaluators/reflect.ts b/src/lib/evaluators/reflect.ts index 2facac8..be00873 100644 --- a/src/lib/evaluators/reflect.ts +++ b/src/lib/evaluators/reflect.ts @@ -5,8 +5,7 @@ import { type BgentRuntime } from "../runtime"; import { type Action, type Actor, type Message, type State } from "../types"; import { parseJsonArrayFromText } from "../utils"; -// Used in the reflection step -const template = `TASK: FACT SUMMARIZATION ("Reflection") +const template = `TASK: Fact Summarization Extract what happened in the scene as an array of claims in JSON format. These are an examples of the expected output of this task: @@ -99,7 +98,7 @@ Correct response format: # END OF INSTRUCTIONS -# START OF ACTUAL TASK +# START OF ACTUAL TASK INFORMATION Facts about the scene: {{recentReflections}} @@ -115,10 +114,6 @@ Scene Dialog: INSTRUCTIONS: Extract any claims from the conversation in the scene that are not already present in the list of facts.`; -/** - * Summarizes the last event into a list of JSON entries, utility for the Rolodex feature - * @TODO - Rework moon's factual json reflection system (rolodex) - */ async function handler(runtime: BgentRuntime, message: Message) { const state = (await runtime.composeState(message)) as State; @@ -165,7 +160,6 @@ async function handler(runtime: BgentRuntime, message: Message) { let reflections = null; - // loop 3 times, call runtime.completion, and parse the result, if result is null try again, if result is valid continue for (let i = 0; i < 3; i++) { const reflectionText: string = await runtime.completion({ context, @@ -205,7 +199,6 @@ async function handler(runtime: BgentRuntime, message: Message) { }) .map((reflection) => reflection.claim); - // break up the reflection into multiple memories for (const reflection of filteredReflections) { const reflectionMemory = await runtime.reflectionManager.addEmbeddingToMemory({ @@ -217,7 +210,6 @@ async function handler(runtime: BgentRuntime, message: Message) { await runtime.reflectionManager.createMemory(reflectionMemory, true); - // wait for .2 seconds await new Promise((resolve) => setTimeout(resolve, 250)); } return filteredReflections; @@ -229,7 +221,6 @@ export default { _runtime: BgentRuntime, _message: Message, ): Promise => { - // immediatel resolve true return await Promise.resolve(true); }, description: diff --git a/src/lib/goals.ts b/src/lib/goals.ts index 15f9f30..33c4a64 100644 --- a/src/lib/goals.ts +++ b/src/lib/goals.ts @@ -30,7 +30,6 @@ export const getGoals = async ({ }; export const formatGoalsAsString = async ({ goals }: { goals: Goal[] }) => { - // format goals as a string const goalStrings = goals.map((goal: Goal) => { const header = `${goal.name} - ${goal.status}`; const objectives = goal.objectives.map((objective: Objective) => { diff --git a/src/lib/index.ts b/src/lib/index.ts index 49a3fd4..2f1a367 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1,12 +1,12 @@ -export { BgentRuntime } from "./runtime"; - +export * from "./actions"; export * from "./context"; +export * from "./evaluation"; export * from "./goals"; -export * from "./messages"; +export * from "./logger"; export * from "./memory"; -export * from "./actions"; +export * from "./messages"; export * from "./relationships"; -export * from "./evaluation"; -export * from "./utils"; -export * from "./logger"; +export * from "./runtime"; export * from "./types"; +export * from "./utils"; + diff --git a/src/lib/logger.ts b/src/lib/logger.ts index 25451b4..6a39006 100644 --- a/src/lib/logger.ts +++ b/src/lib/logger.ts @@ -1,4 +1,644 @@ -// import chalk from 'chalk' +// @ts-nocheck + +import process from 'node:process'; +import os from 'node:os'; +import tty from 'node:tty'; + +// From: https://github.com/sindresorhus/has-flag/blob/main/index.js +/// function hasFlag(flag, argv = globalThis.Deno?.args ?? process.argv) { +function hasFlag(flag: string, argv = globalThis.Deno ? globalThis.Deno.args : process.argv) { + const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--'); + const position = argv.indexOf(prefix + flag); + const terminatorPosition = argv.indexOf('--'); + return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition); +} + +const {env} = process as unknown as { [x: string]: string }; + +let flagForceColor: number; +if ( + hasFlag('no-color') + || hasFlag('no-colors') + || hasFlag('color=false') + || hasFlag('color=never') +) { + flagForceColor = 0; +} else if ( + hasFlag('color') + || hasFlag('colors') + || hasFlag('color=true') + || hasFlag('color=always') +) { + flagForceColor = 1; +} + +function envForceColor() { + if ('FORCE_COLOR' in env) { + if (env.FORCE_COLOR === 'true') { + return 1; + } + + if (env.FORCE_COLOR === 'false') { + return 0; + } + + return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3); + } +} + +function translateLevel(level: number) { + if (level === 0) { + return false; + } + + return { + level, + hasBasic: true, + has256: level >= 2, + has16m: level >= 3, + }; +} + +function _supportsColor(haveStream: any, {streamIsTTY, sniffFlags = true} = {}) { + const noFlagForceColor = envForceColor(); + if (noFlagForceColor !== undefined) { + flagForceColor = noFlagForceColor; + } + + const forceColor = sniffFlags ? flagForceColor : noFlagForceColor; + + if (forceColor === 0) { + return 0; + } + + if (sniffFlags) { + if (hasFlag('color=16m') + || hasFlag('color=full') + || hasFlag('color=truecolor')) { + return 3; + } + + if (hasFlag('color=256')) { + return 2; + } + } + + // Check for Azure DevOps pipelines. + // Has to be above the `!streamIsTTY` check. + if ('TF_BUILD' in env && 'AGENT_NAME' in env) { + return 1; + } + + if (haveStream && !streamIsTTY && forceColor === undefined) { + return 0; + } + + const min = forceColor || 0; + + if (env.TERM === 'dumb') { + return min; + } + + if (process.platform === 'win32') { + // Windows 10 build 10586 is the first Windows release that supports 256 colors. + // Windows 10 build 14931 is the first release that supports 16m/TrueColor. + const osRelease = os.release().split('.'); + if ( + Number(osRelease[0]) >= 10 + && Number(osRelease[2]) >= 10_586 + ) { + return Number(osRelease[2]) >= 14_931 ? 3 : 2; + } + + return 1; + } + + if ('CI' in env) { + if ('GITHUB_ACTIONS' in env || 'GITEA_ACTIONS' in env) { + return 3; + } + + if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI', 'BUILDKITE', 'DRONE'].some(sign => sign in env) || env.CI_NAME === 'codeship') { + return 1; + } + + return min; + } + + if ('TEAMCITY_VERSION' in env) { + return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; + } + + if (env.COLORTERM === 'truecolor') { + return 3; + } + + if (env.TERM === 'xterm-kitty') { + return 3; + } + + if ('TERM_PROGRAM' in env) { + const version = Number.parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10); + + switch (env.TERM_PROGRAM) { + case 'iTerm.app': { + return version >= 3 ? 3 : 2; + } + + case 'Apple_Terminal': { + return 2; + } + // No default + } + } + + if (/-256(color)?$/i.test(env.TERM)) { + return 2; + } + + if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) { + return 1; + } + + if ('COLORTERM' in env) { + return 1; + } + + return min; +} + +export function createSupportsColor(stream: { isTTY: any; }, options = {}) { + const level = _supportsColor(stream, { + streamIsTTY: stream && stream.isTTY, + ...options, + }); + + return translateLevel(level); +} + +const supportsColor = { + stdout: createSupportsColor({isTTY: tty.isatty(1)}), + stderr: createSupportsColor({isTTY: tty.isatty(2)}), +}; + +const ANSI_BACKGROUND_OFFSET = 10; + +const wrapAnsi16 = (offset = 0) => (code: number) => `\u001B[${code + offset}m`; + +const wrapAnsi256 = (offset = 0) => (code: any) => `\u001B[${38 + offset};5;${code}m`; + +const wrapAnsi16m = (offset = 0) => (red: any, green: any, blue: any) => `\u001B[${38 + offset};2;${red};${green};${blue}m`; + +const styles = { + modifier: { + reset: [0, 0], + // 21 isn't widely supported and 22 does the same thing + bold: [1, 22], + dim: [2, 22], + italic: [3, 23], + underline: [4, 24], + overline: [53, 55], + inverse: [7, 27], + hidden: [8, 28], + strikethrough: [9, 29], + }, + color: { + black: [30, 39], + red: [31, 39], + green: [32, 39], + yellow: [33, 39], + blue: [34, 39], + magenta: [35, 39], + cyan: [36, 39], + white: [37, 39], + + // Bright color + blackBright: [90, 39], + gray: [90, 39], // Alias of `blackBright` + grey: [90, 39], // Alias of `blackBright` + redBright: [91, 39], + greenBright: [92, 39], + yellowBright: [93, 39], + blueBright: [94, 39], + magentaBright: [95, 39], + cyanBright: [96, 39], + whiteBright: [97, 39], + }, + bgColor: { + bgBlack: [40, 49], + bgRed: [41, 49], + bgGreen: [42, 49], + bgYellow: [43, 49], + bgBlue: [44, 49], + bgMagenta: [45, 49], + bgCyan: [46, 49], + bgWhite: [47, 49], + + // Bright color + bgBlackBright: [100, 49], + bgGray: [100, 49], // Alias of `bgBlackBright` + bgGrey: [100, 49], // Alias of `bgBlackBright` + bgRedBright: [101, 49], + bgGreenBright: [102, 49], + bgYellowBright: [103, 49], + bgBlueBright: [104, 49], + bgMagentaBright: [105, 49], + bgCyanBright: [106, 49], + bgWhiteBright: [107, 49], + }, +}; + +export const modifierNames = Object.keys(styles.modifier); +export const foregroundColorNames = Object.keys(styles.color); +export const backgroundColorNames = Object.keys(styles.bgColor); +export const colorNames = [...foregroundColorNames, ...backgroundColorNames]; + +function assembleStyles() { + const codes = new Map(); + + for (const [groupName, group] of Object.entries(styles)) { + for (const [styleName, style] of Object.entries(group)) { + styles[styleName] = { + open: `\u001B[${style[0]}m`, + close: `\u001B[${style[1]}m`, + }; + + group[styleName] = styles[styleName]; + + codes.set(style[0], style[1]); + } + + Object.defineProperty(styles, groupName, { + value: group, + enumerable: false, + }); + } + + Object.defineProperty(styles, 'codes', { + value: codes, + enumerable: false, + }); + + styles.color.close = '\u001B[39m'; + styles.bgColor.close = '\u001B[49m'; + + styles.color.ansi = wrapAnsi16(); + styles.color.ansi256 = wrapAnsi256(); + styles.color.ansi16m = wrapAnsi16m(); + styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET); + styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET); + styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET); + + // From https://github.com/Qix-/color-convert/blob/3f0e0d4e92e235796ccb17f6e85c72094a651f49/conversions.js + Object.defineProperties(styles, { + rgbToAnsi256: { + value(red: number, green: number, blue: number) { + // We use the extended greyscale palette here, with the exception of + // black and white. normal palette only has 4 greyscale shades. + if (red === green && green === blue) { + if (red < 8) { + return 16; + } + + if (red > 248) { + return 231; + } + + return Math.round(((red - 8) / 247) * 24) + 232; + } + + return 16 + + (36 * Math.round(red / 255 * 5)) + + (6 * Math.round(green / 255 * 5)) + + Math.round(blue / 255 * 5); + }, + enumerable: false, + }, + hexToRgb: { + value(hex: { toString: (arg0: number) => string; }) { + const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16)); + if (!matches) { + return [0, 0, 0]; + } + + let [colorString] = matches; + + if (colorString.length === 3) { + colorString = [...colorString].map(character => character + character).join(''); + } + + const integer = Number.parseInt(colorString, 16); + + return [ + /* eslint-disable no-bitwise */ + (integer >> 16) & 0xFF, + (integer >> 8) & 0xFF, + integer & 0xFF, + /* eslint-enable no-bitwise */ + ]; + }, + enumerable: false, + }, + hexToAnsi256: { + value: (hex: any) => styles.rgbToAnsi256(...styles.hexToRgb(hex)), + enumerable: false, + }, + ansi256ToAnsi: { + value(code: number) { + if (code < 8) { + return 30 + code; + } + + if (code < 16) { + return 90 + (code - 8); + } + + let red; + let green; + let blue; + + if (code >= 232) { + red = (((code - 232) * 10) + 8) / 255; + green = red; + blue = red; + } else { + code -= 16; + + const remainder = code % 36; + + red = Math.floor(code / 36) / 5; + green = Math.floor(remainder / 6) / 5; + blue = (remainder % 6) / 5; + } + + const value = Math.max(red, green, blue) * 2; + + if (value === 0) { + return 30; + } + + // eslint-disable-next-line no-bitwise + let result = 30 + ((Math.round(blue) << 2) | (Math.round(green) << 1) | Math.round(red)); + + if (value === 2) { + result += 60; + } + + return result; + }, + enumerable: false, + }, + rgbToAnsi: { + value: (red: any, green: any, blue: any) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)), + enumerable: false, + }, + hexToAnsi: { + value: (hex: any) => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)), + enumerable: false, + }, + }); + + return styles; +} + +const ansiStyles = assembleStyles(); + +const {stdout: stdoutColor, stderr: stderrColor} = supportsColor; + +const GENERATOR = Symbol('GENERATOR'); +const STYLER = Symbol('STYLER'); +const IS_EMPTY = Symbol('IS_EMPTY'); + +// `supportsColor.level` → `ansiStyles.color[name]` mapping +const levelMapping = [ + 'ansi', + 'ansi', + 'ansi256', + 'ansi16m', +]; + +const styles2 = Object.create(null); + +function stringReplaceAll(string: string, substring: string | any[], replacer: any) { + let index = string.indexOf(substring); + if (index === -1) { + return string; + } + + const substringLength = substring.length; + let endIndex = 0; + let returnValue = ''; + do { + returnValue += string.slice(endIndex, index) + substring + replacer; + endIndex = index + substringLength; + index = string.indexOf(substring, endIndex); + } while (index !== -1); + + returnValue += string.slice(endIndex); + return returnValue; +} + +function stringEncaseCRLFWithFirstIndex(string: string | string[], prefix: any, postfix: string, index: number) { + let endIndex = 0; + let returnValue = ''; + do { + const gotCR = string[index - 1] === '\r'; + returnValue += string.slice(endIndex, (gotCR ? index - 1 : index)) + prefix + (gotCR ? '\r\n' : '\n') + postfix; + endIndex = index + 1; + index = string.indexOf('\n', endIndex); + } while (index !== -1); + + returnValue += string.slice(endIndex); + return returnValue; +} + +const applyOptions = (object: { (...strings: any[]): string; level?: any; }, options = {}) => { + if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) { + throw new Error('The `level` option should be an integer from 0 to 3'); + } + + // Detect level if not set manually + const colorLevel = stdoutColor ? stdoutColor.level : 0; + object.level = options.level === undefined ? colorLevel : options.level; +}; + +export class Chalk { + constructor(options: any) { + // eslint-disable-next-line no-constructor-return + return chalkFactory(options); + } +} + +const chalkFactory = (options: {} | undefined) => { + const chalk = (...strings: any[]) => strings.join(' '); + applyOptions(chalk, options); + + Object.setPrototypeOf(chalk, createChalk.prototype); + + return chalk; +}; + +function createChalk(options: { level: any; } | undefined) { + return chalkFactory(options); +} + +Object.setPrototypeOf(createChalk.prototype, Function.prototype); + +for (const [styleName, style] of Object.entries(ansiStyles)) { + styles2[styleName] = { + get() { + const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]); + Object.defineProperty(this, styleName, {value: builder}); + return builder; + }, + }; +} + +styles2.visible = { + get() { + const builder = createBuilder(this, this[STYLER], true); + Object.defineProperty(this, 'visible', {value: builder}); + return builder; + }, +}; + +const getModelAnsi = (model: string, level: string, type: string, ...arguments_: any[]) => { + if (model === 'rgb') { + if (level === 'ansi16m') { + return ansiStyles[type].ansi16m(...arguments_); + } + + if (level === 'ansi256') { + return ansiStyles[type].ansi256(ansiStyles.rgbToAnsi256(...arguments_)); + } + + return ansiStyles[type].ansi(ansiStyles.rgbToAnsi(...arguments_)); + } + + if (model === 'hex') { + return getModelAnsi('rgb', level, type, ...ansiStyles.hexToRgb(...arguments_)); + } + + return ansiStyles[type][model](...arguments_); +}; + +const usedModels = ['rgb', 'hex', 'ansi256']; + +for (const model of usedModels) { + styles2[model] = { + get() { + const {level} = this; + return function (...arguments_: any) { + const styler = createStyler(getModelAnsi(model, levelMapping[level], 'color', ...arguments_), ansiStyles.color.close, this[STYLER]); + return createBuilder(this, styler, this[IS_EMPTY]); + }; + }, + }; + + const bgModel = 'bg' + model[0].toUpperCase() + model.slice(1); + styles2[bgModel] = { + get() { + const {level} = this; + return function (...arguments_: any) { + const styler = createStyler(getModelAnsi(model, levelMapping[level], 'bgColor', ...arguments_), ansiStyles.bgColor.close, this[STYLER]); + return createBuilder(this, styler, this[IS_EMPTY]); + }; + }, + }; +} + +const proto = Object.defineProperties(() => {}, { + ...styles2, + level: { + enumerable: true, + get() { + return this[GENERATOR].level; + }, + set(level) { + this[GENERATOR].level = level; + }, + }, +}); + +const createStyler = (open: any, close: any, parent: { openAll: any; closeAll: any; } | undefined) => { + let openAll; + let closeAll; + if (parent === undefined) { + openAll = open; + closeAll = close; + } else { + openAll = parent.openAll + open; + closeAll = close + parent.closeAll; + } + + return { + open, + close, + openAll, + closeAll, + parent, + }; +}; + +const createBuilder = (self: any, _styler: { open: any; close: any; openAll: any; closeAll: any; parent: any; }, _isEmpty: boolean) => { + // Single argument is hot path, implicit coercion is faster than anything + // eslint-disable-next-line no-implicit-coercion + const builder = (...arguments_: string[]) => applyStyle(builder, (arguments_.length === 1) ? ('' + arguments_[0]) : arguments_.join(' ')); + + // We alter the prototype because we must return a function, but there is + // no way to create a function with a different prototype + Object.setPrototypeOf(builder, proto); + + builder[GENERATOR] = self; + builder[STYLER] = _styler; + builder[IS_EMPTY] = _isEmpty; + + return builder; +}; + +const applyStyle = (self: { (...arguments_: any[]): any;[x: string]: any; "__@GENERATOR@162242"?: any; "__@STYLER@162087"?: any; "__@IS_EMPTY@162243"?: any; level?: any; }, string: string | string[]) => { + if (self.level <= 0 || !string) { + return self[IS_EMPTY] ? '' : string; + } + + let styler = self[STYLER]; + + if (styler === undefined) { + return string; + } + + const {openAll, closeAll} = styler; + if (string.includes('\u001B')) { + while (styler !== undefined) { + // Replace any instances already present with a re-opening code + // otherwise only the part of the string until said closing code + // will be colored, and the rest will simply be 'plain'. + string = stringReplaceAll(string, styler.close, styler.open); + + styler = styler.parent; + } + } + + // We can move both next actions out of loop, because remaining actions in loop won't have + // any/visible effect on parts we add here. Close the styling before a linebreak and reopen + // after next line to fix a bleed issue on macOS: https://github.com/chalk/chalk/pull/92 + const lfIndex = string.indexOf('\n'); + if (lfIndex !== -1) { + string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex); + } + + return openAll + string + closeAll; +}; + +Object.defineProperties(createChalk.prototype, styles2); + +const chalk = createChalk(); +export const chalkStderr = createChalk({level: stderrColor ? stderrColor.level : 0}); + +export { + stdoutColor as supportsColor, + stderrColor as supportsColorStderr, +}; class Logger { frameChar = "*"; @@ -12,12 +652,10 @@ class Logger { }: { title?: string; frame?: boolean; - color?: string; + color?: ForegroundColorName }, ): void { - console.log("color", color); - // @ts-ignore - const coloredMessage = message; + const coloredMessage = chalk[color](message); if (frame) { const framedMessage = this.frameMessage(coloredMessage, title); console.log(framedMessage); diff --git a/src/lib/memory.ts b/src/lib/memory.ts index 4a00fa3..529efe1 100644 --- a/src/lib/memory.ts +++ b/src/lib/memory.ts @@ -134,7 +134,6 @@ export class MemoryManager { } async removeMemory(memoryId: UUID): Promise { - // remove item const result = await this.runtime.supabase .from(this.tableName) .delete() diff --git a/src/lib/messages.ts b/src/lib/messages.ts index d4fec83..9aa43bb 100644 --- a/src/lib/messages.ts +++ b/src/lib/messages.ts @@ -3,10 +3,6 @@ import { messageExamples } from "./messageExamples"; import { type SupabaseClient } from "@supabase/supabase-js"; import { type UUID } from "crypto"; -/** Get the actors who are participating in the message, for context injection of name and description - * agents is the array of default agents to search from - * userIds are UUIDs of users, stored in DB - */ export async function getMessageActors({ supabase, userIds, @@ -25,7 +21,6 @@ export async function getMessageActors({ const { data } = response; - // join the data from the database with the data from the exampleNpcs const actors = data.map((actor: Actor) => { const { name, details, id } = actor; return { @@ -39,7 +34,6 @@ export async function getMessageActors({ } export function formatMessageActors({ actors }: { actors: Actor[] }) { - // format actors as a string const actorStrings = actors.map((actor: Actor) => { const header = `${actor.name}: ${actor.details.tagline}\n${actor.details.summary}`; return header; @@ -52,14 +46,12 @@ export function formatMessageActors({ actors }: { actors: Actor[] }) { * return an array of random conversation examples from the messageExamples array */ export const getRandomMessageExamples = (count: number) => { - // return an array of random conversation examples from the messageExamples array const examples: Array< Array< | { user: string; content: string; action: null | undefined } | { user: string; content: string; action: string } > > = []; - // make sure the examples are not duplicated while (examples.length < count && examples.length < messageExamples.length) { const randomIndex = Math.floor(Math.random() * messageExamples.length); const randomExample = messageExamples[randomIndex]; @@ -68,8 +60,6 @@ export const getRandomMessageExamples = (count: number) => { } } - // exampe messages is an array of arrays of objects - // format the examples so that each object is on one line const formattedExamples = examples.map((example) => { return `\n${example .map((message) => { @@ -81,7 +71,6 @@ export const getRandomMessageExamples = (count: number) => { return formattedExamples.join("\n"); }; -// format conversation as string export const formatMessages = ({ messages, actors, @@ -89,7 +78,6 @@ export const formatMessages = ({ messages: Memory[]; actors: Actor[]; }) => { - // format conversation as a string const messageStrings = messages .reverse() .filter((message: Memory) => message.user_id) @@ -103,9 +91,7 @@ export const formatMessages = ({ return messageStrings; }; -/** format conversation as string */ export const formatReflections = (reflections: Memory[]) => { - // format conversation as a string const messageStrings = reflections .reverse() .map( diff --git a/src/lib/relationships.ts b/src/lib/relationships.ts index 00ae898..4a5bdd8 100644 --- a/src/lib/relationships.ts +++ b/src/lib/relationships.ts @@ -14,8 +14,6 @@ export async function createRelationship({ userA: UUID; userB: UUID; }) { - // create a connection - // return the connection const { data, error } = await supabase.from("relationships").upsert({ user_a: userA, user_b: userB, @@ -56,11 +54,9 @@ export async function getRelationships({ supabase: SupabaseClient; userId: string; }) { - // Await the query to complete and get the response directly const { data, error } = await supabase .from("relationships") .select("*") - // Check for userId in either user_a or user_b columns .or(`user_a.eq.${userId},user_b.eq.${userId}`) .eq("status", "FRIENDS"); diff --git a/src/lib/runtime.ts b/src/lib/runtime.ts index 1892024..24bbe10 100644 --- a/src/lib/runtime.ts +++ b/src/lib/runtime.ts @@ -19,8 +19,6 @@ import { type Message, } from "./types"; import { parseJSONObjectFromText, parseJsonArrayFromText } from "./utils"; -// State can be passed around to different parts of the agent to provide context for decision making -// State is also passed converted into a context object via template injection to generate a response with the LLM import { formatActionConditions, @@ -170,7 +168,6 @@ export class BgentRuntime { requestOptions, ); - // if response has an error if (!response.ok) { throw new Error( "OpenAI API Error: " + response.status + " " + response.statusText, @@ -259,7 +256,6 @@ export class BgentRuntime { stop: [], }); - // log the response await this.supabase .from("logs") .insert({ @@ -323,7 +319,7 @@ export class BgentRuntime { )!; if (!action) { - return; // console.warn('No action found for', data.action) + return console.warn('No action found for', data.action) } if (!action.handler) { @@ -387,9 +383,6 @@ export class BgentRuntime { } async evaluate(message: Message, state: State) { - // 3. make sure all the evaluators have conditionals and desciptions - // 4. write test and validate - const evaluatorPromises = this.evaluators.map( async (evaluator: Evaluator) => { if (!evaluator.handler) { @@ -408,7 +401,6 @@ export class BgentRuntime { const resolvedEvaluators = await Promise.all(evaluatorPromises); const evaluatorsData = resolvedEvaluators.filter(Boolean); - // format the evaluators const evaluators = formatEvaluators(evaluatorsData as Evaluator[]); const evaluatorNames = formatEvaluatorNames(evaluatorsData as Evaluator[]); const evaluatorConditions = formatEvaluatorConditions( @@ -424,7 +416,6 @@ export class BgentRuntime { context, }); - // parse the result array const parsedResult = parseJsonArrayFromText(result); this.evaluators diff --git a/src/lib/templates.ts b/src/lib/templates.ts index 6d10164..68d9a16 100644 --- a/src/lib/templates.ts +++ b/src/lib/templates.ts @@ -1,4 +1,3 @@ -// Respond to user input export const requestHandlerTemplate = `## Example Messages json\`\`\` {{messageExamples}} diff --git a/src/lib/types.ts b/src/lib/types.ts index 266843e..ca0bd6d 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -71,7 +71,6 @@ export interface State { [key: string]: unknown; } -// what onMessage etc receive export interface Message { agentId: UUID; senderId: UUID; diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 1720c58..cab5f16 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -2,8 +2,6 @@ import { type Content, type Memory, type State } from "./types"; export function shouldSkipMessage(state: State, agentId: string): boolean { if (state.recentMessagesData && state.recentMessagesData.length > 2) { - // read the last messages - // if the last 3 messages are from the agent, or the last message from the agent has the WAIT action, then we should skip const currentMessages = state.recentMessagesData ?? []; const lastThreeMessages = currentMessages.slice(-3); const lastThreeMessagesFromAgent = lastThreeMessages.filter( @@ -12,7 +10,7 @@ export function shouldSkipMessage(state: State, agentId: string): boolean { if (lastThreeMessagesFromAgent.length === 3) { return true; } - // if the last two messages had the WAIT action from current agent, then we should skip + const lastTwoMessagesFromAgent = lastThreeMessagesFromAgent.slice(-2); const lastTwoMessagesFromAgentWithWaitAction = lastTwoMessagesFromAgent.filter( @@ -28,35 +26,28 @@ export function shouldSkipMessage(state: State, agentId: string): boolean { export function parseJsonArrayFromText(text: string) { let jsonData = null; - // Check for json block const jsonBlockPattern = /```json\n([\s\S]*?)\n```/; const jsonBlockMatch = text.match(jsonBlockPattern); if (jsonBlockMatch) { - // Extract and parse json block content try { jsonData = JSON.parse(jsonBlockMatch[1]); } catch (e) { - // If parsing fails, return null return null; } } else { - // Check for array-like structure without json block const arrayPattern = /\[\s*{[\s\S]*?}\s*\]/; const arrayMatch = text.match(arrayPattern); if (arrayMatch) { - // Extract and parse array content try { jsonData = JSON.parse(arrayMatch[0]); } catch (e) { - // If parsing fails, return null return null; } } } - // Check if parsed data is an array and has the expected structure if ( Array.isArray(jsonData) && jsonData.every( @@ -65,7 +56,6 @@ export function parseJsonArrayFromText(text: string) { ) { return jsonData; } else { - // If data is invalid or does not meet the expected structure, return null return null; } } @@ -73,35 +63,28 @@ export function parseJsonArrayFromText(text: string) { export function parseJSONObjectFromText(text: string) { let jsonData = null; - // Check for json block const jsonBlockPattern = /```json\n([\s\S]*?)\n```/; const jsonBlockMatch = text.match(jsonBlockPattern); if (jsonBlockMatch) { - // Extract and parse json block content try { jsonData = JSON.parse(jsonBlockMatch[1]); } catch (e) { - // If parsing fails, return null return null; } } else { - // Check for object-like structure without json block const objectPattern = /{[\s\S]*?}/; const objectMatch = text.match(objectPattern); if (objectMatch) { - // Extract and parse object content try { jsonData = JSON.parse(objectMatch[0]); } catch (e) { - // If parsing fails, return null return null; } } } - // Check if parsed data is an object (and not an array) if ( typeof jsonData === "object" && jsonData !== null && @@ -111,7 +94,6 @@ export function parseJSONObjectFromText(text: string) { } else if (typeof jsonData === "object" && Array.isArray(jsonData)) { return parseJsonArrayFromText(text); } else { - // If data is invalid or is not an object, return null return null; } } diff --git a/src/test/createRuntime.ts b/src/test/createRuntime.ts index 6d4e949..fbd589f 100644 --- a/src/test/createRuntime.ts +++ b/src/test/createRuntime.ts @@ -13,7 +13,6 @@ export async function createRuntime( ) { const supabase = createClient(SUPABASE_URL!, SUPABASE_ANON_KEY!); - // login const { data: { user, session }, } = await supabase.auth.signInWithPassword({