diff --git a/package.json b/package.json index 3de0c1b..389e0bf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bgent", - "version": "0.0.10", + "version": "0.0.11", "private": false, "description": "bgent. because agent was taken.", "type": "module", diff --git a/scripts/concat.mjs b/scripts/concat.mjs index 40fc3ff..7dd61a2 100644 --- a/scripts/concat.mjs +++ b/scripts/concat.mjs @@ -5,7 +5,7 @@ import { fileURLToPath } from 'url' const instructions = 'The above code was taken from my codebase at https://github.com/lalalune/bgent. You are writing tests and documentation for my codebase. Please use the above code as a reference. Tests should be written with Jest and Typescript. Do not use mocks or stubs. Keep it very simple and straightforward.' // Patterns to ignore -const ignorePatterns = ['actionExamples.ts', 'agents', 'goal', 'goals', 'utils', 'logger', 'index', 'data', 'constants', 'templates', 'worker'] +const ignorePatterns = ['actionExamples.ts', 'agents', 'goal', 'cache', 'goals', 'supabase', 'utils', 'logger', 'index', 'data', 'constants', 'templates', 'worker'] // __dirname is not defined in ES module scope, so we need to create it const __filename = fileURLToPath(import.meta.url) @@ -24,7 +24,7 @@ const shouldIgnore = (filePath) => { // Function to recursively read through directories and concatenate .ts files const readDirectory = (dirPath) => { - let concatenatedContent = '# START MY CODEBASE' + let concatenatedContent = '' fs.readdirSync(dirPath).forEach(file => { const filePath = path.join(dirPath, file) @@ -51,7 +51,7 @@ const readDirectory = (dirPath) => { } // Start reading from the root TypeScript directory -const concatenatedContent = readDirectory(directoryPath) +const concatenatedContent = '# START MY CODEBASE' + readDirectory(directoryPath) // Write the concatenated content to the output file fs.writeFileSync(outputFile, concatenatedContent + '# END MY CODEBASE\n\n' + instructions) diff --git a/src/agents/cj/actions/__tests__/introduce.test.ts b/src/agents/cj/actions/__tests__/introduce.test.ts index 27bdbed..6199e83 100644 --- a/src/agents/cj/actions/__tests__/introduce.test.ts +++ b/src/agents/cj/actions/__tests__/introduce.test.ts @@ -23,9 +23,9 @@ const zeroUuid = "00000000-0000-0000-0000-000000000000" as UUID; describe("Introduce Action", () => { test("Introduce the user", async () => { - const { user, runtime } = await createRuntime( - process.env as Record, - ); + const { user, runtime } = await createRuntime({ + env: process.env as Record, + }); const data = await getRelationship({ runtime, diff --git a/src/agents/cj/evaluators/__tests__/details.test.ts b/src/agents/cj/evaluators/__tests__/details.test.ts index 8e73f8a..c32df5d 100644 --- a/src/agents/cj/evaluators/__tests__/details.test.ts +++ b/src/agents/cj/evaluators/__tests__/details.test.ts @@ -21,10 +21,9 @@ const zeroUuid = "00000000-0000-0000-0000-000000000000" as UUID; describe("User Details", () => { test("Get user details", async () => { - const { user, runtime } = await createRuntime( - process.env as Record, - 24, - ); + const { user, runtime } = await createRuntime({ + env: process.env as Record, + }); const data = await getRelationship({ runtime, diff --git a/src/agents/cj/evaluators/__tests__/profile.test.ts b/src/agents/cj/evaluators/__tests__/profile.test.ts index 9841d14..47b7392 100644 --- a/src/agents/cj/evaluators/__tests__/profile.test.ts +++ b/src/agents/cj/evaluators/__tests__/profile.test.ts @@ -28,7 +28,9 @@ let user: User; describe("User Profile", () => { beforeAll(async () => { - const result = await createRuntime(); + const result = await createRuntime({ + env: process.env as Record, + }); runtime = result.runtime; user = result.session.user; }); @@ -56,9 +58,9 @@ describe("User Profile", () => { }); test("Get user profile", async () => { - const { user, runtime } = await createRuntime( - process.env as Record, - ); + const { user, runtime } = await createRuntime({ + env: process.env as Record, + }); const data = await getRelationship({ runtime, @@ -164,7 +166,7 @@ describe("User Profile", () => { expect( result.toLowerCase().includes("startup") || - result.toLowerCase().includes("programmer"), + result.toLowerCase().includes("programmer"), ).toBe(true); const descriptions = await runtime.descriptionManager.getMemoriesByIds({ diff --git a/src/lib/__tests__/actions.test.ts b/src/lib/__tests__/actions.test.ts index 4698b33..00aba9f 100644 --- a/src/lib/__tests__/actions.test.ts +++ b/src/lib/__tests__/actions.test.ts @@ -2,8 +2,10 @@ import { type User } from "@supabase/supabase-js"; import { type UUID } from "crypto"; import dotenv from "dotenv"; import { createRuntime } from "../../test/createRuntime"; +import { TEST_ACTION, TEST_ACTION_FAIL } from "../../test/testAction"; import { getRelationship } from "../relationships"; import { type BgentRuntime } from "../runtime"; +import { type Message } from "../types"; dotenv.config(); @@ -12,24 +14,27 @@ const zeroUuid = "00000000-0000-0000-0000-000000000000" as UUID; describe("Actions", () => { let user: User; let runtime: BgentRuntime; - // let room_id: UUID; + let room_id: UUID; afterAll(async () => { await cleanup(); }); beforeAll(async () => { - const setup = await createRuntime(); - user = setup.session.user; - runtime = setup.runtime; + const { session, runtime: _runtime } = await createRuntime({ + env: process.env as Record, + actions: [TEST_ACTION, TEST_ACTION_FAIL], + }); + user = session.user; + runtime = _runtime; - // const data = await getRelationship({ - // runtime, - // userA: user?.id as UUID, - // userB: zeroUuid, - // }); + const data = await getRelationship({ + runtime, + userA: user?.id as UUID, + userB: zeroUuid, + }); - // room_id = data?.room_id; + room_id = data?.room_id; await cleanup(); }); @@ -45,96 +50,88 @@ describe("Actions", () => { ]); } - describe("Actions", () => { - let user: User; - let runtime: BgentRuntime; - let room_id: UUID; - - afterAll(async () => { - await cleanup(); - }); - - beforeAll(async () => { - const setup = await createRuntime(); - user = setup.session.user; - runtime = setup.runtime; + // Test that actions are being loaded into context properly + test("Actions are loaded into context", async () => { + const actions = runtime.actions; + expect(actions).toBeDefined(); + expect(actions.length).toBeGreaterThan(0); + // Ensure the CONTINUE action is part of the loaded actions + const continueAction = actions.find((action) => action.name === "CONTINUE"); + expect(continueAction).toBeDefined(); + }); - const data = await getRelationship({ - runtime, - userA: user?.id as UUID, - userB: zeroUuid, + // Test that actions are validated properly + test("Continue action is always valid", async () => { + const continueAction = runtime.actions.find( + (action) => action.name === "CONTINUE", + ); + expect(continueAction).toBeDefined(); + if (continueAction && continueAction.validate) { + const isValid = await continueAction.validate(runtime, { + agentId: zeroUuid, + senderId: user.id as UUID, + userIds: [user.id as UUID, zeroUuid], + content: "Test message", + room_id: room_id, }); + expect(isValid).toBeTruthy(); + } else { + throw new Error( + "Continue action or its validation function is undefined", + ); + } + }); - room_id = data?.room_id; + test("Validate that TEST_ACTION is in the state", async () => { + const message: Message = { + senderId: user.id as UUID, + agentId: zeroUuid, + userIds: [user.id as UUID, zeroUuid], + content: + "Please respond with the message 'ok' and the action TEST_ACTION", + room_id, + }; - await cleanup(); - }); + const response = await runtime.handleRequest(message); - async function cleanup() { - await runtime.summarizationManager.removeAllMemoriesByUserIds([ - user?.id as UUID, - zeroUuid, - ]); - await runtime.messageManager.removeAllMemoriesByUserIds([ - user?.id as UUID, - zeroUuid, - ]); - } + expect(response.action).toEqual("TEST_ACTION"); + }); - // Test that actions are being loaded into context properly - test("Actions are loaded into context", async () => { - const actions = runtime.getActions(); - expect(actions).toBeDefined(); - expect(actions.length).toBeGreaterThan(0); - // Ensure the CONTINUE action is part of the loaded actions - const continueAction = actions.find( - (action) => action.name === "CONTINUE", - ); - expect(continueAction).toBeDefined(); - }); + test("Test that actions are properly validated in state", async () => { + const message: Message = { + senderId: user.id as UUID, + agentId: zeroUuid, + userIds: [user.id as UUID, zeroUuid], + content: + "Please respond with the message 'ok' and the action TEST_ACTION", + room_id, + }; - // Test that actions are validated properly - test("Continue action is always valid", async () => { - const continueAction = runtime - .getActions() - .find((action) => action.name === "CONTINUE"); - expect(continueAction).toBeDefined(); - if (continueAction && continueAction.validate) { - const isValid = await continueAction.validate(runtime, { - agentId: zeroUuid, - senderId: user.id as UUID, - userIds: [user.id as UUID, zeroUuid], - content: "Test message", - room_id: room_id, - }); - expect(isValid).toBeTruthy(); - } else { - throw new Error( - "Continue action or its validation function is undefined", - ); - } - }); + const state = await runtime.composeState(message); + expect(state.actionNames).not.toContain("TEST_ACTION_FAIL"); - // Test that action handlers are being called properly - test("Continue action handler is called", async () => { - const continueAction = runtime - .getActions() - .find((action) => action.name === "CONTINUE"); - expect(continueAction).toBeDefined(); - if (continueAction && continueAction.handler) { - const mockMessage = { - agentId: zeroUuid, - senderId: user.id as UUID, - userIds: [user.id as UUID, zeroUuid], - content: "Test message for CONTINUE action", - room_id: room_id, - }; - const response = await continueAction.handler(runtime, mockMessage); - expect(response).toBeDefined(); - // Further assertions can be made based on the expected outcome of the CONTINUE action handler - } else { - throw new Error("Continue action or its handler function is undefined"); - } - }, 20000); + expect(state.actionNames).toContain("TEST_ACTION"); }); + + // Test that action handlers are being called properly + test("Continue action handler is called", async () => { + const continueAction = runtime.actions.find( + (action) => action.name === "CONTINUE", + ); + expect(continueAction).toBeDefined(); + if (continueAction && continueAction.handler) { + const mockMessage = { + agentId: zeroUuid, + senderId: user.id as UUID, + userIds: [user.id as UUID, zeroUuid], + content: "Test message for CONTINUE action", + room_id: room_id, + }; + const response = await continueAction.handler(runtime, mockMessage); + expect(response).toBeDefined(); + // Further assertions can be made based on the expected outcome of the CONTINUE action handler + } else { + throw new Error("Continue action or its handler function is undefined"); + } + }, 20000); }); diff --git a/src/lib/__tests__/evaluation.test.ts b/src/lib/__tests__/evaluation.test.ts index 5dfd240..ea23921 100644 --- a/src/lib/__tests__/evaluation.test.ts +++ b/src/lib/__tests__/evaluation.test.ts @@ -1,84 +1,156 @@ -import { type User } from "@supabase/supabase-js"; -import { BgentRuntime } from "../runtime"; -import { type Message } from "../types"; +import { User } from "@supabase/supabase-js"; +import { UUID } from "crypto"; +import dotenv from "dotenv"; import { createRuntime } from "../../test/createRuntime"; -import { type UUID } from "crypto"; +import { TEST_EVALUATOR, TEST_EVALUATOR_FAIL } from "../../test/testEvaluator"; +import { composeContext } from "../context"; +import { evaluationTemplate } from "../evaluation"; +import summarization from "../evaluators/summarization"; +import { getRelationship } from "../relationships"; +import { BgentRuntime } from "../runtime"; +import { Message } from "../types"; + +dotenv.config(); describe("Evaluation Process", () => { let runtime: BgentRuntime; let user: User; - const zeroUuid = "00000000-0000-0000-0000-000000000000" as UUID; + let room_id: UUID; + const zeroUuid: UUID = "00000000-0000-0000-0000-000000000000"; beforeAll(async () => { - const setup = await createRuntime(); + const setup = await createRuntime({ + env: process.env as Record, + evaluators: [TEST_EVALUATOR, TEST_EVALUATOR_FAIL], + }); runtime = setup.runtime; user = setup.session.user; - // Assuming the evaluator 'summary' is already registered in the runtime setup + const relationship = await getRelationship({ + runtime, + userA: user.id as UUID, + userB: zeroUuid, + }); + room_id = relationship?.room_id; + }); + + test("Validate the format of the examples from the evaluator", () => { + expect(TEST_EVALUATOR.examples).toBeInstanceOf(Array); + TEST_EVALUATOR.examples.forEach((example) => { + expect(example).toHaveProperty("context"); + expect(example).toHaveProperty("messages"); + expect(example.messages).toBeInstanceOf(Array); + example.messages.forEach((message) => { + expect(message).toHaveProperty("user"); + expect(message).toHaveProperty("content"); + expect(message).toHaveProperty("action"); + }); + expect(example).toHaveProperty("outcome"); + }); }); - test("Evaluation Injection - Evaluator Creates Memory", async () => { + test("Check if test and examples appear in state", async () => { const message: Message = { senderId: user.id as UUID, agentId: zeroUuid, - userIds: [user?.id as UUID, zeroUuid], - content: "Trigger evaluator content", - room_id: zeroUuid, + userIds: [user.id as UUID, zeroUuid], + content: "Test message for evaluation", + room_id, }; - await runtime.handleRequest(message); + const state = await runtime.composeState(message); + const prompt = composeContext({ state, template: evaluationTemplate }); - // Assuming the 'summary' evaluator tags the memories it creates with 'summarization' - const memories = await runtime.summarizationManager.getMemoriesByIds({ - userIds: [user.id as UUID, zeroUuid], - count: 1, - }); + // expect that the prompt contacts the testEvaluator name + expect(prompt).toContain(TEST_EVALUATOR.name); + + // check if state.EvaluatorNames contains the testEvaluator name - // Expect at least one memory to be created with the 'summarization' tag - expect(memories.length).toBeGreaterThan(0); + expect(state.evaluatorNames).toContain(TEST_EVALUATOR.name); }); - test("Evaluator Not Running if No Evaluation Handlers are True", async () => { + test("Run the TEST_EVALUATOR handler and validate output", async () => { const message: Message = { - senderId: user?.id as UUID, + senderId: user.id as UUID, agentId: zeroUuid, - userIds: [user?.id as UUID, zeroUuid], - content: "Non-triggering content", - room_id: zeroUuid, + userIds: [user.id as UUID, zeroUuid], + content: "Run TEST_EVALUATOR handler", + room_id, }; - await runtime.handleRequest(message); + const result = await TEST_EVALUATOR.handler(runtime, message); + expect(result).toBeTruthy(); + }); - // Assuming the 'summary' evaluator tags the memories it creates with 'summarization' - const memories = await runtime.summarizationManager.getMemoriesByIds({ - userIds: [user.id as UUID, zeroUuid], - count: 10, + // load up a conversation + // call the prompt + // get back the array of evaluators + // run the evaluators + // check the results + test("Run the evaluation process", async () => { + const { runtime } = await createRuntime({ + env: process.env as Record, + evaluators: [TEST_EVALUATOR, TEST_EVALUATOR_FAIL], }); - // Assuming the previous test ran and created exactly one memory - // Expect the number of memories to remain unchanged - expect(memories.length).toBe(1); + const message: Message = { + senderId: user.id as UUID, + agentId: zeroUuid, + userIds: [user.id as UUID, zeroUuid], + content: "Please run the test evaluator", + room_id, + }; + + const state = await runtime.composeState(message); + + const result = await runtime.evaluate(message, state); + + expect(result?.includes("TEST_EVALUATOR")).toBe(true); }); - test("Evaluation Handling and Response - Evaluator Updates Memory", async () => { + test("Run the evaluation process", async () => { + const { runtime } = await createRuntime({ + env: process.env as Record, + evaluators: [TEST_EVALUATOR, TEST_EVALUATOR_FAIL], + }); + const message: Message = { senderId: user.id as UUID, agentId: zeroUuid, userIds: [user.id as UUID, zeroUuid], - content: "Content that leads to a specific evaluator response", - room_id: zeroUuid, + content: "Please run the test evaluator", + room_id, }; - await runtime.handleRequest(message); + const state = await runtime.composeState(message); - // Assuming the 'summary' evaluator updates the 'content' of memories it processes - // Fetch the updated memory - const memories = await runtime.summarizationManager.getMemoriesByIds({ - userIds: [user.id as UUID, zeroUuid], - count: 1, + const result = await runtime.evaluate(message, state); + + expect(result?.includes("TEST_EVALUATOR")).toBe(true); + }); + + test("Test that summarization appears in evaluation handler", async () => { + const { runtime } = await createRuntime({ + env: process.env as Record, + recentMessageCount: 1, }); - // Expect the updated memory to contain specific content - expect(memories[0].content).toContain("specific content"); + const message: Message = { + senderId: user.id as UUID, + agentId: zeroUuid, + userIds: [user.id as UUID, zeroUuid], + content: "Test message for evaluation", + room_id, + }; + + const state = await runtime.composeState(message); + const prompt = composeContext({ state, template: evaluationTemplate }); + + // expect that the prompt contacts the testEvaluator name + expect(prompt).toContain(summarization.name); + + // check if state.EvaluatorNames contains the testEvaluator name + + expect(state.evaluatorNames).toContain(summarization.name); }); }); diff --git a/src/lib/__tests__/memory.test.ts b/src/lib/__tests__/memory.test.ts index 662965b..dab6035 100644 --- a/src/lib/__tests__/memory.test.ts +++ b/src/lib/__tests__/memory.test.ts @@ -16,7 +16,9 @@ describe("Memory", () => { let room_id: UUID | null = null; beforeAll(async () => { - const result = await createRuntime(); + const result = await createRuntime({ + env: process.env as Record, + }); runtime = result.runtime; user = result.session.user; @@ -203,7 +205,9 @@ describe("Memory - Basic tests", () => { // Setup before all tests beforeAll(async () => { - const result = await createRuntime(); + const result = await createRuntime({ + env: process.env as Record, + }); runtime = result.runtime; user = result.session.user; @@ -293,7 +297,9 @@ describe("Memory - Extended Tests", () => { let room_id: UUID | null = null; beforeAll(async () => { - const result = await createRuntime(); + const result = await createRuntime({ + env: process.env as Record, + }); runtime = result.runtime; user = result.session.user; diff --git a/src/lib/__tests__/messages.test.ts b/src/lib/__tests__/messages.test.ts index 81d89c0..b8e72c7 100644 --- a/src/lib/__tests__/messages.test.ts +++ b/src/lib/__tests__/messages.test.ts @@ -14,7 +14,9 @@ describe("Messages Library", () => { let runtime: BgentRuntime, user: User, actors: Actor[]; beforeAll(async () => { - const setup = await createRuntime(); + const setup = await createRuntime({ + env: process.env as Record, + }); runtime = setup.runtime; user = setup.session.user; actors = await getMessageActors({ diff --git a/src/lib/__tests__/relationships.test.ts b/src/lib/__tests__/relationships.test.ts index aa0aa60..684c55b 100644 --- a/src/lib/__tests__/relationships.test.ts +++ b/src/lib/__tests__/relationships.test.ts @@ -18,7 +18,9 @@ describe("Relationships Module", () => { let user: User; beforeAll(async () => { - const setup = await createRuntime(); + const setup = await createRuntime({ + env: process.env as Record, + }); runtime = setup.runtime; user = setup.session.user; }); diff --git a/src/lib/__tests__/runtime.test.ts b/src/lib/__tests__/runtime.test.ts index 680d2a9..fa8c0a4 100644 --- a/src/lib/__tests__/runtime.test.ts +++ b/src/lib/__tests__/runtime.test.ts @@ -48,7 +48,9 @@ describe("Agent Runtime", () => { // Set up before each test beforeEach(async () => { - const result = await createRuntime(); + const result = await createRuntime({ + env: process.env as Record, + }); runtime = result.runtime; user = result.session.user; diff --git a/src/lib/actions/__tests__/continue.test.ts b/src/lib/actions/__tests__/continue.test.ts index 111b282..bea9029 100644 --- a/src/lib/actions/__tests__/continue.test.ts +++ b/src/lib/actions/__tests__/continue.test.ts @@ -44,7 +44,9 @@ describe("User Profile", () => { }); beforeAll(async () => { - const setup = await createRuntime(); + const setup = await createRuntime({ + env: process.env as Record, + }); user = setup.session.user; runtime = setup.runtime; diff --git a/src/lib/actions/__tests__/ignore.test.ts b/src/lib/actions/__tests__/ignore.test.ts index d22454e..5e1db80 100644 --- a/src/lib/actions/__tests__/ignore.test.ts +++ b/src/lib/actions/__tests__/ignore.test.ts @@ -27,7 +27,9 @@ describe("Ignore action tests", () => { }); beforeAll(async () => { - const setup = await createRuntime(); + const setup = await createRuntime({ + env: process.env as Record, + }); user = setup.session.user; runtime = setup.runtime; diff --git a/src/lib/actions/__tests__/wait.test.ts b/src/lib/actions/__tests__/wait.test.ts index f9750ae..e980acd 100644 --- a/src/lib/actions/__tests__/wait.test.ts +++ b/src/lib/actions/__tests__/wait.test.ts @@ -27,7 +27,9 @@ describe("Wait Action Behavior", () => { }); beforeAll(async () => { - const setup = await createRuntime(); + const setup = await createRuntime({ + env: process.env as Record, + }); user = setup.session.user; runtime = setup.runtime; diff --git a/src/lib/evaluation.ts b/src/lib/evaluation.ts index 1566121..fb9df23 100644 --- a/src/lib/evaluation.ts +++ b/src/lib/evaluation.ts @@ -57,3 +57,72 @@ export function formatEvaluatorConditions(evaluators: Evaluator[]) { ) .join(",\n"); } + +import { uniqueNamesGenerator, names } from "unique-names-generator"; + +// Formats evaluator examples into a readable string +export function formatEvaluatorExamples(evaluators: Evaluator[]) { + return evaluators + .map((evaluator) => { + return evaluator.examples + .map((example) => { + const exampleNames = Array.from({ length: 5 }, () => + uniqueNamesGenerator({ dictionaries: [names] }), + ); + + let formattedContext = example.context; + let formattedOutcome = example.outcome; + + exampleNames.forEach((name, index) => { + const placeholder = `{{user${index + 1}}}`; + formattedContext = formattedContext.replaceAll(placeholder, name); + formattedOutcome = formattedOutcome.replaceAll(placeholder, name); + }); + + const formattedMessages = example.messages + .map((message) => { + let messageString = `${message.user}: ${message.content}`; + exampleNames.forEach((name, index) => { + const placeholder = `{{user${index + 1}}}`; + messageString = messageString.replaceAll(placeholder, name); + }); + return ( + messageString + (message.action ? ` (${message.action})` : "") + ); + }) + .join("\n"); + + return `Context:\n${formattedContext}\n\nMessages:\n${formattedMessages}\n\nOutcome:\n${formattedOutcome}`; + }) + .join("\n\n"); + }) + .join("\n\n"); +} + +// Generates a string describing the conditions under which each evaluator example is relevant +export function formatEvaluatorExampleConditions(evaluators: Evaluator[]) { + return evaluators + .map((evaluator) => + evaluator.examples + .map( + (_example, index) => + `${evaluator.name} Example ${index + 1}: ${evaluator.condition}`, + ) + .join("\n"), + ) + .join("\n\n"); +} + +// Generates a string summarizing the descriptions of each evaluator example +export function formatEvaluatorExampleDescriptions(evaluators: Evaluator[]) { + return evaluators + .map((evaluator) => + evaluator.examples + .map( + (_example, index) => + `${evaluator.name} Example ${index + 1}: ${evaluator.description}`, + ) + .join("\n"), + ) + .join("\n\n"); +} diff --git a/src/lib/evaluators/__tests__/summarization.test.ts b/src/lib/evaluators/__tests__/summarization.test.ts index b6c193b..b98b137 100644 --- a/src/lib/evaluators/__tests__/summarization.test.ts +++ b/src/lib/evaluators/__tests__/summarization.test.ts @@ -9,11 +9,11 @@ import { GetTellMeAboutYourselfConversation3, jimFacts, } from "../../../test/data"; +import { populateMemories } from "../../../test/populateMemories"; import { getRelationship } from "../../relationships"; import { type BgentRuntime } from "../../runtime"; import { type Message } from "../../types"; import evaluator from "../summarization"; -import { populateMemories } from "test/populateMemories"; dotenv.config(); @@ -29,7 +29,9 @@ describe("Factual Summarization", () => { }); beforeAll(async () => { - const setup = await createRuntime(); + const setup = await createRuntime({ + env: process.env as Record, + }); user = setup.session.user; runtime = setup.runtime; diff --git a/src/lib/evaluators/summarization.ts b/src/lib/evaluators/summarization.ts index 5f78c53..c5026fb 100644 --- a/src/lib/evaluators/summarization.ts +++ b/src/lib/evaluators/summarization.ts @@ -26,100 +26,31 @@ export const formatSummarizations = (summarizations: Memory[]) => { 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: - # START OF EXAMPLES -""" -Example Scene Dialog: -Eric: Just found a rare artifact in the wilds! -Jim: Awesome! What does it do? -Eric: It's a rare sword that gives +10 to all stats! -Jim: whoah thats insane -Eric: I know right? I'm never going to sell it lol - -Claims: -\`\`\`json -[ - {claim: 'Eric found a rare sword in the wilds that gives +10 to all stats.', type: 'fact', in_bio: false, already_known: false }, - {claim: 'Eric is never going to sell his new rare artifact sword', 'type': 'status', in_bio: false, already_known: false }, -] -\`\`\` -""" -Facts about the scene: -Alex and Kim are meeting up for coffee -Alex and Kim have been friends for a long time - -Actors in the scene: -Alex - Marathon runner and gymnist. Worked out every day for a year to prepare for a marathon. Friends with Kim. -Kim - Friends with Alex. Likes shopping and going to the beach. Has a dog named Spot. - -Example Scene Dialog: -alex: I finally completed the marathon this year! -kim: That's amazing! How long did it take you? -alex: Just under 4 hours, which was my goal! -kim: That's so impressive, I know you worked out all year for that -alex: Yeah, I'm really proud of myself. 2 hours a day at the gym for a year! - -Claims: -json\`\`\` -[ - { "claim": "Alex just completed a marathon in just under 4 hours.", "type": "fact", "in_bio": false, "already_known": false }, - { "claim": "Alex worked out 2 hours a day at the gym for a year.", "type": "fact", "in_bio": true, "already_known": false }, - { "claim": "Alex is really proud of himself.", "type": "status", "in_bio": false, "already_known": false } -] -\`\`\` -""" -Facts about the Scene -Mike and Eva won a regional chess tournament about six months ago -Mike and Eva are friends - -Actors in the Scene: -mike - Chess club president. Likes to play chess and go to the park. Friends with Eva. -eva - Friends with Mike. Likes to play chess and go to the park. Chess club member. - -Scene Dialog: -mike: Remember when we won the regional chess tournament last spring? -eva: Of course! That was an incredible day. -mike: It really put our chess club on the map. - -Claims: -json\`\`\` -[ - { "claim": "Mike and Eva won the regional chess tournament last spring", "type": "fact", "in_bio": false, "already_known": true }, - { "claim": "Winning the regional chess tournament put the chess club on the map", "type": "status", "in_bio": false, "already_known": false } -] -\`\`\` +These are an examples of the expected output of this task: +{{evaluationExamples}} # END OF EXAMPLES -Note: The above was all example dialogue. Ignore it for the actual scene. +Note: The above is examples of how to perform the task (fewshot). DO NOT USE for the actual task. + Below is the information that will be used for the task. -# START OF INSTRUCTIONS +# INSTRUCTIONS Extract any claims from the conversation in the ACTUAL scene that are not already present in the list of facts. - If the fact is already in the character's description, set in_bio to true - If the fact is already known to the character, set already_known to true -- Set the type to fact or status +- Set the type to 'fact' or 'opinion' +- For facts, set the type to 'fact' +- For non-facts, set the type to 'opinion' - Facts are always true, facts about the world or the character that do not change -- Status is pertinent to the current scene or character's immediate situation, also includes the character's thoughts, feelings, judgments or recommendations -- Response should be a JSON object array inside a JSON markdown block +- 'opinion' inlcudes non-factual opinions and also includes the character's thoughts, feelings, judgments or recommendations - Ignore the examples when considering facts - Include any factual detail, including where the user lives, works, or goes to school, what they do for a living, their hobbies, and any other relevant information -Correct response format: -\`\`\`json -[ - {claim: string, type: enum, in_bio: boolean, already_known: boolean }, - {claim: string, type: enum, in_bio: boolean, already_known: boolean }, - ... -] -\`\`\` - -# END OF INSTRUCTIONS - # START OF ACTUAL TASK INFORMATION -Facts about the scene: +Facts about the actors: {{recentSummarizations}} {{relevantSummarizations}} @@ -133,11 +64,11 @@ Scene Dialog: INSTRUCTIONS: Extract ALL claims from the conversation in the scene that are not already present in the list of facts. -Correct response format: +Response should be a JSON object array inside a JSON markdown block. Correct response format: \`\`\`json [ - {claim: string, type: enum, in_bio: boolean, already_known: boolean }, - {claim: string, type: enum, in_bio: boolean, already_known: boolean }, + {claim: string, type: enum, in_bio: boolean, already_known: boolean }, + {claim: string, type: enum, in_bio: boolean, already_known: boolean }, ... ] \`\`\``; @@ -155,13 +86,10 @@ async function handler(runtime: BgentRuntime, message: Message) { const agentName = actors?.find((actor: Actor) => actor.id === agentId)?.name; - const actionNames = runtime - .getActions() - .map((a: Action) => a.name) - .join(", "); + const actionNames = runtime.actions.map((a: Action) => a.name).join(", "); + console.log("actionNames", actionNames); - const actions = runtime - .getActions() + const actions = runtime.actions .map((a: Action) => `${a.name}: ${a.description}`) .join("\n"); @@ -247,16 +175,123 @@ export default { name: "SUMMARIZE", validate: async ( // eslint-disable-next-line @typescript-eslint/no-unused-vars - _runtime: BgentRuntime, + runtime: BgentRuntime, // eslint-disable-next-line @typescript-eslint/no-unused-vars - _message: Message, + message: Message, ): Promise => { - return await Promise.resolve(true); + const messageCount = (await runtime.messageManager.countMemoriesByUserIds( + message.userIds, + )) as number; + + const reflectionCount = Math.ceil(runtime.getRecentMessageCount() / 2); + + return messageCount % reflectionCount === 0; }, description: "Extract factual information about the people in the conversation, the current events in the world, and anything else that might be important to remember.", condition: "New factual information was revealed in the recent conversation which should be remembered.", handler, - examples: [], + examples: [ + { + context: `Actors in the scene: +{{user1}}: Programmer and moderator of the local story club. +{{user2}}: New member of the club. Likes to write and read. + +Facts about the actors: +None`, + messages: [ + { + user: "{{user1}}", + content: "So where are you from?", + action: "WAIT", + }, + { + user: "{{user2}}", + content: "I'm from the city.", + }, + { + user: "{{user1}}", + content: "Which city?", + }, + { + user: "{{user2}}", + content: "Oakland", + }, + { + user: "{{user1}}", + content: "Oh, I've never been there, but I know it's in California!", + }, + ], + outcome: `{ "claim": "{{user1}} is from Oakland", "type": "fact", "in_bio": false, "already_known": false },`, + }, + { + context: `Actors in the scene: +{{user1}}: Athelete and cyclist. Worked out every day for a year to prepare for a marathon. +{{user2}}: Likes to go to the beach and shop. + +Facts about the actors: +{{user1}} and {{user2}} are talking about the marathon +{{user1}} and {{user2}} have just started dating`, + messages: [ + { + user: "{{user1}}", + content: "I finally completed the marathon this year!", + }, + { + user: "{{user2}}", + content: "Wow! How long did it take?", + }, + { + user: "{{user1}}", + content: "A little over three hours.", + }, + { + user: "{{user1}}", + content: "I'm so proud of myself.", + }, + ], + outcome: `Claims: +json\`\`\` +[ + { "claim": "Alex just completed a marathon in just under 4 hours.", "type": "fact", "in_bio": false, "already_known": false }, + { "claim": "Alex worked out 2 hours a day at the gym for a year.", "type": "fact", "in_bio": true, "already_known": false }, + { "claim": "Alex is really proud of himself.", "type": "opinion", "in_bio": false, "already_known": false } +] +\`\`\` +`, + }, + { + context: `Actors in the scene: +{{user1}}: Likes to play poker and go to the park. Friends with Eva. +{{user2}}: Also likes to play poker. Likes to write and read. + +Facts about the actors: +Mike and Eva won a regional poker tournament about six months ago +Mike is married to Alex +Eva studied Philosophy before switching to Computer Science`, + messages: [ + { + user: "{{user1}}", + content: + "Remember when we won the regional poker tournament last spring?", + }, + { + user: "{{user2}}", + content: "Of course! That was an incredible day.", + }, + { + user: "{{user1}}", + content: "It really put our poker club on the map.", + }, + ], + outcome: `Claims: +json\`\`\` +[ + { "claim": "Mike and Eva won the regional poker tournament last spring", "type": "fact", "in_bio": false, "already_known": true }, + { "claim": "Winning the regional poker tournament put the poker club on the map", "type": "opinion", "in_bio": false, "already_known": false } +] +\`\`\``, + }, + ], }; diff --git a/src/lib/runtime.ts b/src/lib/runtime.ts index 1d31101..ab56b84 100644 --- a/src/lib/runtime.ts +++ b/src/lib/runtime.ts @@ -5,6 +5,7 @@ import { defaultEvaluators, evaluationTemplate, formatEvaluatorConditions, + formatEvaluatorExamples, formatEvaluatorNames, formatEvaluators, } from "./evaluation"; @@ -85,27 +86,17 @@ export class BgentRuntime { this.token = opts.token; - defaultActions.forEach((action) => { - this.registerAction(action); - }); - - const actions = opts.actions ?? []; - actions.forEach((action: Action) => { - if (!this.getActions().includes(action)) { + [...defaultActions, ...((opts.actions ?? []) as Action[])].forEach( + (action) => { this.registerAction(action); - } - }); - - defaultEvaluators.forEach((evaluator) => { - this.registerEvaluator(evaluator); - }); + }, + ); - const evaluators = opts.evaluators ?? []; - evaluators.forEach((evaluator: Evaluator) => { - if (!this.evaluators.includes(evaluator)) { - this.evaluators.push(evaluator); - } - }); + [...defaultEvaluators, ...((opts.evaluators ?? []) as Evaluator[])].forEach( + (evaluator) => { + this.registerEvaluator(evaluator); + }, + ); } getRecentMessageCount() { @@ -116,18 +107,10 @@ export class BgentRuntime { this.actions.push(action); } - getActions() { - return [...new Set(this.actions)]; - } - registerEvaluator(evaluator: Evaluator) { this.evaluators.push(evaluator); } - getEvaluationHandlers() { - return [...new Set(this.evaluators)]; - } - async completion({ context = "", stop = [], @@ -179,7 +162,7 @@ export class BgentRuntime { } return content; } catch (error) { - console.log("e", error); + console.error("ERROR:", error); throw new Error(error as string); } } @@ -217,7 +200,7 @@ export class BgentRuntime { return data?.data?.[0].embedding; } catch (e) { - console.log("e", e); + console.error(e); throw e; } } @@ -234,9 +217,6 @@ export class BgentRuntime { template: requestHandlerTemplate, }); - console.log("*** runtime context"); - console.log(context); - if (this.debugMode) { logger.log(context, { title: "Response Context", @@ -249,15 +229,10 @@ export class BgentRuntime { const { senderId, room_id, userIds: user_ids, agentId } = message; for (let triesLeft = 3; triesLeft > 0; triesLeft--) { - console.log("*** context"); const response = await this.completion({ context, stop: [], }); - console.log("*** response"); - - console.log("*** runtime response"); - console.log(response); this.supabase .from("logs") @@ -300,7 +275,7 @@ export class BgentRuntime { return; } - const action = this.getActions().find( + const action = this.actions.find( (a: { name: string }) => a.name === data.action, )!; @@ -368,8 +343,7 @@ export class BgentRuntime { const evaluatorPromises = this.evaluators.map( async (evaluator: Evaluator) => { if (!evaluator.handler) { - console.log("no handler"); - return; + return null; } const result = await evaluator.validate(this, message, state); @@ -383,6 +357,11 @@ export class BgentRuntime { const resolvedEvaluators = await Promise.all(evaluatorPromises); const evaluatorsData = resolvedEvaluators.filter(Boolean); + // if there are no evaluators this frame, return + if (evaluatorsData.length === 0) { + return []; + } + const evaluators = formatEvaluators(evaluatorsData as Evaluator[]); const evaluatorNames = formatEvaluatorNames(evaluatorsData as Evaluator[]); const evaluatorConditions = formatEvaluatorConditions( @@ -401,23 +380,26 @@ export class BgentRuntime { const parsedResult = parseJsonArrayFromText(result); this.evaluators - .filter( - (evaluator: Evaluator) => - evaluator.handler && parsedResult?.includes(evaluator.name), - ) + .filter((evaluator: Evaluator) => parsedResult?.includes(evaluator.name)) .forEach((evaluator: Evaluator) => { if (!evaluator?.handler) return; evaluator.handler(this, message); }); + + return parsedResult; } async composeState(message: Message) { const { senderId, agentId, userIds, room_id } = message; const recentMessageCount = this.getRecentMessageCount(); - const recentSummarizationsCount = this.getRecentMessageCount() / 2; - const relevantSummarizationsCount = this.getRecentMessageCount() / 2; + const recentSummarizationsCount = Math.ceil( + this.getRecentMessageCount() / 2, + ); + const relevantSummarizationsCount = Math.ceil( + this.getRecentMessageCount() / 2, + ); const [ actorsData, @@ -506,12 +488,7 @@ export class BgentRuntime { relevantSummarizationsData, }; - const actionPromises = this.getActions().map(async (action: Action) => { - if (!action.handler) { - console.log("no handler"); - return; - } - + const actionPromises = this.actions.map(async (action: Action) => { const result = await action.validate(this, message); if (result) { return action; @@ -519,7 +496,24 @@ export class BgentRuntime { return null; }); + const evaluatorPromises = this.evaluators.map(async (evaluator) => { + const result = await evaluator.validate(this, message, initialState); + if (result) { + return evaluator; + } + return null; + }); + + const resolvedEvaluators = await Promise.all(evaluatorPromises); + + const evaluatorsData = resolvedEvaluators.filter(Boolean) as Evaluator[]; + const evaluators = formatEvaluators(evaluatorsData); + const evaluatorNames = formatEvaluatorNames(evaluatorsData); + const evaluatorConditions = formatEvaluatorConditions(evaluatorsData); + const evaluatorExamples = formatEvaluatorExamples(evaluatorsData); + const resolvedActions = await Promise.all(actionPromises); + const actionsData = resolvedActions.filter(Boolean) as Action[]; const actionState = { @@ -527,6 +521,11 @@ export class BgentRuntime { actionConditions: formatActionConditions(actionsData), actions: formatActions(actionsData), actionExamples: composeActionExamples(actionsData, 10), + evaluatorsData, + evaluators, + evaluatorNames, + evaluatorConditions, + evaluatorExamples, }; return { ...initialState, ...actionState }; diff --git a/src/lib/types.ts b/src/lib/types.ts index 47f994a..4991325 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -105,12 +105,25 @@ export type Validator = ( ) => Promise; export interface Action { - name: string; - description: string; condition: string; + description: string; examples: ContentExample[][]; + handler: Handler; + name: string; validate: Validator; - handler: Handler | undefined; } -export interface Evaluator extends Action {} +export type EvaluationExample = { + context: string; + messages: Array; + outcome: string; +}; + +export interface Evaluator { + condition: string; + description: string; + examples: EvaluationExample[]; + handler: Handler; + name: string; + validate: Validator; +} diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 633bb48..5badf78 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -23,12 +23,9 @@ export function parseJsonArrayFromText(text: string) { } } - if ( - Array.isArray(jsonData) && - jsonData.every( - (item) => typeof item === "object" && "claim" in item && "type" in item, - ) - ) { + console.log("jsonData", jsonData); + + if (Array.isArray(jsonData)) { return jsonData; } else { return null; diff --git a/src/supabase/db.sql b/src/supabase/db.sql new file mode 100644 index 0000000..6decf9a --- /dev/null +++ b/src/supabase/db.sql @@ -0,0 +1,778 @@ + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +CREATE SCHEMA IF NOT EXISTS "public"; + +ALTER SCHEMA "public" OWNER TO "pg_database_owner"; + +CREATE OR REPLACE FUNCTION "public"."check_similarity_and_insert"(query_table_name text, query_user_id uuid, query_user_ids uuid[], query_content jsonb, query_room_id uuid, query_embedding extensions.vector, similarity_threshold double precision) RETURNS void + LANGUAGE "plpgsql" + AS $$ +DECLARE + similar_found BOOLEAN := FALSE; + select_query TEXT; + insert_query TEXT; +BEGIN + -- Only perform the similarity check if query_embedding is not NULL + IF query_embedding IS NOT NULL THEN + -- Build a dynamic query to check for existing similar embeddings using cosine distance + select_query := format( + 'SELECT EXISTS (' || + 'SELECT 1 ' || + 'FROM %I ' || + 'WHERE user_id = %L ' || + 'AND user_ids @> %L ' || + 'AND user_ids <@ %L ' || + 'AND embedding <=> %L < %L ' || -- Using cosine distance and comparing with threshold + 'LIMIT 1' || + ')', + query_table_name, + query_user_id, + query_user_ids, + query_embedding, + similarity_threshold + ); + + -- Execute the query to check for similarity + EXECUTE select_query INTO similar_found; + END IF; + + -- Prepare the insert query with 'unique' field set based on the presence of similar records or NULL query_embedding + insert_query := format( + 'INSERT INTO %I (user_id, user_ids, content, room_id, embedding, "unique") ' || + 'VALUES (%L, %L, %L, %L, %L, %L)', + query_table_name, + query_user_id, + query_user_ids, + query_content, + query_room_id, + query_embedding, + NOT similar_found OR query_embedding IS NULL -- Set 'unique' to true if no similar record is found or query_embedding is NULL + ); + + -- Execute the insert query + EXECUTE insert_query; +END; +$$; + +ALTER FUNCTION "public"."check_similarity_and_insert"(query_table_name text, query_user_id uuid, query_user_ids uuid[], query_content jsonb, query_room_id uuid, query_embedding extensions.vector, similarity_threshold double precision) OWNER TO "postgres"; + +CREATE OR REPLACE FUNCTION "public"."count_memories"(query_table_name text, query_user_ids uuid[], query_unique boolean DEFAULT false) RETURNS bigint + LANGUAGE "plpgsql" + AS $$ +DECLARE + query TEXT; + total BIGINT; +BEGIN + -- Initialize the base query + query := format('SELECT COUNT(*) FROM %I WHERE TRUE', query_table_name); + + -- Add condition for user_ids if not null, ensuring proper spacing + IF query_user_ids IS NOT NULL THEN + query := query || format(' AND user_ids @> %L::uuid[]', query_user_ids); + END IF; + + -- Add condition for unique if TRUE, ensuring proper spacing + IF query_unique THEN + query := query || ' AND "unique" = TRUE'; -- Use double quotes if "unique" is a reserved keyword or potentially problematic + END IF; + + -- Debug: Output the constructed query + RAISE NOTICE 'Executing query: %', query; + + -- Execute the constructed query + EXECUTE query INTO total; + RETURN total; +END; +$$; + +ALTER FUNCTION "public"."count_memories"(query_table_name text, query_user_ids uuid[], query_unique boolean) OWNER TO "postgres"; + +CREATE OR REPLACE FUNCTION "public"."create_dm_room"() RETURNS trigger + LANGUAGE "plpgsql" + AS $$ +DECLARE + new_room_id UUID; +BEGIN + IF NEW.status = 'FRIENDS' AND NEW.room_id IS NULL THEN + INSERT INTO rooms (created_by, name) + VALUES (NULL, 'Direct Message') + RETURNING id INTO new_room_id; + + UPDATE friendships + SET room_id = new_room_id + WHERE id = NEW.id; + + INSERT INTO participants (user_id, room_id) + VALUES (NEW.user_id_1, new_room_id), (NEW.user_id_2, new_room_id); + END IF; + + RETURN NEW; +END; +$$; + +ALTER FUNCTION "public"."create_dm_room"() OWNER TO "postgres"; + +CREATE OR REPLACE FUNCTION "public"."create_friendship_and_room_for_user"(p_new_user_id uuid) RETURNS void + LANGUAGE "plpgsql" + AS $$ +DECLARE + host_agent_id UUID := '00000000-0000-0000-0000-000000000000'; + new_room_id UUID; +BEGIN + -- Create a new room for the direct message between the specified user and the host agent + INSERT INTO rooms (created_by, name, is_dm) + VALUES (p_new_user_id, 'Direct Message with Host Agent', TRUE) + RETURNING id INTO new_room_id; + + -- Create a new friendship between the specified user and the host agent + INSERT INTO relationships (user_id_1, user_id_2, status, room_id) + VALUES (p_new_user_id, host_agent_id, 'FRIENDS', new_room_id); + + -- Add both users as participants of the new room + INSERT INTO participants (user_id, room_id) + VALUES (p_new_user_id, new_room_id), (host_agent_id, new_room_id); +END; +$$; + +ALTER FUNCTION "public"."create_friendship_and_room_for_user"(p_new_user_id uuid) OWNER TO "postgres"; + +CREATE OR REPLACE FUNCTION "public"."create_friendship_with_host_agent"() RETURNS trigger + LANGUAGE "plpgsql" + AS $$ +DECLARE + host_agent_id UUID := '00000000-0000-0000-0000-000000000000'; + new_room_id UUID; +BEGIN + -- Create a new room for the direct message between the new user and the host agent + INSERT INTO rooms (created_by, name) + VALUES (NEW.id, 'Direct Message with Host Agent') + RETURNING id INTO new_room_id; + + -- Create a new friendship between the new user and the host agent + INSERT INTO relationships (user_a, user_b, user_id, status, room_id) + VALUES (NEW.id, host_agent_id, host_agent_id, 'FRIENDS', new_room_id); + + -- Add both users as participants of the new room + INSERT INTO participants (user_id, room_id) + VALUES (NEW.id, new_room_id), (host_agent_id, new_room_id); + + RETURN NEW; +END; +$$; + +ALTER FUNCTION "public"."create_friendship_with_host_agent"() OWNER TO "postgres"; + +CREATE OR REPLACE FUNCTION "public"."delete_room_and_participants_on_friendship_delete"() RETURNS trigger + LANGUAGE "plpgsql" + AS $$ +BEGIN + DELETE FROM rooms WHERE id = OLD.room_id; + + RETURN OLD; +END; +$$; + +ALTER FUNCTION "public"."delete_room_and_participants_on_friendship_delete"() OWNER TO "postgres"; + +SET default_tablespace = ''; + +SET default_table_access_method = "heap"; + +CREATE TABLE IF NOT EXISTS "public"."goals" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "user_ids" uuid[] DEFAULT '{}'::uuid[] NOT NULL, + "user_id" uuid, + "name" text, + "status" text, + "description" text, + "objectives" jsonb[] DEFAULT '{}'::jsonb[] NOT NULL +); + +ALTER TABLE "public"."goals" OWNER TO "postgres"; + +CREATE OR REPLACE FUNCTION "public"."get_goals_by_user_ids"(query_user_ids uuid[], query_user_id uuid DEFAULT NULL::uuid, only_in_progress boolean DEFAULT true, row_count integer DEFAULT 5) RETURNS SETOF public.goals + LANGUAGE "plpgsql" + AS $$ +BEGIN + RETURN QUERY + SELECT * FROM goals + WHERE + (query_user_id IS NULL OR user_id = query_user_id) + AND (user_ids @> query_user_ids) + AND (NOT only_in_progress OR status = 'IN_PROGRESS') + LIMIT row_count; +END; +$$; + +ALTER FUNCTION "public"."get_goals_by_user_ids"(query_user_ids uuid[], query_user_id uuid, only_in_progress boolean, row_count integer) OWNER TO "postgres"; + +CREATE OR REPLACE FUNCTION "public"."get_memories"(query_table_name text, query_user_ids uuid[], query_count integer, query_unique boolean DEFAULT false) RETURNS TABLE(id uuid, user_id uuid, content jsonb, created_at timestamp with time zone, user_ids uuid[], room_id uuid, embedding extensions.vector) + LANGUAGE "plpgsql" + AS $_$ +DECLARE + query TEXT; +BEGIN + query := format($fmt$ + SELECT + id, + user_id, + content, + created_at, + user_ids, + room_id, + embedding + FROM %I + WHERE TRUE + %s -- Condition for user_ids + %s -- Additional condition for 'unique' column based on query_unique + ORDER BY created_at DESC + LIMIT %L + $fmt$, + query_table_name, + CASE WHEN query_user_ids IS NOT NULL THEN format(' AND user_ids @> %L', query_user_ids) ELSE '' END, + CASE WHEN query_unique THEN ' AND "unique" = TRUE' ELSE '' END, -- Enclose 'unique' in double quotes + query_count + ); + + RETURN QUERY EXECUTE query; +END; +$_$; + +ALTER FUNCTION "public"."get_memories"(query_table_name text, query_user_ids uuid[], query_count integer, query_unique boolean) OWNER TO "postgres"; + +CREATE OR REPLACE FUNCTION "public"."get_message_count"(p_user_id uuid) RETURNS TABLE(room_id uuid, unread_messages_count integer) + LANGUAGE "plpgsql" + AS $$BEGIN + RETURN QUERY + SELECT p.room_id, COALESCE(COUNT(m.id)::integer, 0) AS unread_messages_count + FROM participants p + LEFT JOIN messages m ON p.room_id = m.room_id + WHERE p.user_id = p_user_id + GROUP BY p.room_id; +END; +$$; + +ALTER FUNCTION "public"."get_message_count"(p_user_id uuid) OWNER TO "postgres"; + +CREATE OR REPLACE FUNCTION "public"."get_recent_rows_per_user"(query_table_name text, array_of_uuid_arrays uuid[], n_rows_per_user integer, timestamp_column_name text) RETURNS TABLE(user_id uuid, content jsonb, timestamp_column timestamp without time zone) + LANGUAGE "plpgsql" + AS $_$ +DECLARE + dynamic_query TEXT; + i INT; + uuid_array UUID[]; +BEGIN + -- Initialize the dynamic query with a common table expression (CTE) + dynamic_query := format($f$ + WITH ranked_messages AS ( + SELECT user_id, content, %I, ROW_NUMBER() OVER (PARTITION BY UNNEST(user_ids) ORDER BY %I DESC) AS rn + FROM %I + WHERE FALSE + $f$, timestamp_column_name, timestamp_column_name, query_table_name); + + -- Loop through the array of UUID arrays using a FOR loop + FOR i IN 1..array_length(array_of_uuid_arrays, 1) LOOP + -- Access each UUID array by its index + uuid_array := array_of_uuid_arrays[i]; + + -- Append OR condition to check if user_ids contains all UUIDs in the current array + dynamic_query := dynamic_query || format($f$ + OR user_ids @> %L::uuid[] + $f$, uuid_array); + END LOOP; + + -- Complete the dynamic query by selecting rows where the rank is within the top N for each user + dynamic_query := dynamic_query || format($f$ + ) + SELECT user_id, content, %I AS timestamp_column + FROM ranked_messages + WHERE rn <= %L + $f$, timestamp_column_name, n_rows_per_user); + + -- Execute the dynamic query and return the result set + RETURN QUERY EXECUTE dynamic_query; +END; +$_$; + +ALTER FUNCTION "public"."get_recent_rows_per_user"(query_table_name text, array_of_uuid_arrays uuid[], n_rows_per_user integer, timestamp_column_name text) OWNER TO "postgres"; + +CREATE TABLE IF NOT EXISTS "public"."relationships" ( + "created_at" timestamp with time zone DEFAULT (now() AT TIME ZONE 'utc'::text) NOT NULL, + "user_a" uuid, + "user_b" uuid, + "status" text, + "id" uuid DEFAULT gen_random_uuid() NOT NULL, + "room_id" uuid, + "user_id" uuid NOT NULL +); + +ALTER TABLE "public"."relationships" OWNER TO "postgres"; + +CREATE OR REPLACE FUNCTION "public"."get_relationship"(usera uuid, userb uuid) RETURNS SETOF public.relationships + LANGUAGE "plpgsql" STABLE + AS $$ +BEGIN + RETURN QUERY + SELECT * + FROM relationships + WHERE (user_a = usera AND user_b = userb) + OR (user_a = userb AND user_b = usera); +END; +$$; + +ALTER FUNCTION "public"."get_relationship"(usera uuid, userb uuid) OWNER TO "postgres"; + +CREATE OR REPLACE FUNCTION "public"."handle_new_user"() RETURNS trigger + LANGUAGE "plpgsql" SECURITY DEFINER + SET "search_path" TO 'public' + AS $$ +begin + insert into public.profiles (id, full_name, avatar_url) + values (new.id, new.raw_user_meta_data ->> 'user_name', new.raw_user_meta_data ->> 'avatar_url'); + return new; +end; +$$; + +ALTER FUNCTION "public"."handle_new_user"() OWNER TO "postgres"; + +CREATE OR REPLACE FUNCTION "public"."is_user_participant_in_room"(p_user_id uuid, p_room_id uuid) RETURNS boolean + LANGUAGE "plpgsql" + AS $$ +DECLARE + is_participant BOOLEAN; +BEGIN + SELECT EXISTS ( + SELECT 1 + FROM participants + WHERE user_id = p_user_id AND room_id = p_room_id + ) INTO is_participant; + + RETURN is_participant; +END; +$$; + +ALTER FUNCTION "public"."is_user_participant_in_room"(p_user_id uuid, p_room_id uuid) OWNER TO "postgres"; + +CREATE OR REPLACE FUNCTION "public"."remove_memories"(query_table_name text, query_user_ids uuid[]) RETURNS void + LANGUAGE "plpgsql" + AS $_$DECLARE + dynamic_query TEXT; +BEGIN + -- Construct dynamic SQL to delete memories where user_ids contains all elements of query_user_ids + dynamic_query := format('DELETE FROM %I WHERE user_ids @> $1', query_table_name); + + -- Execute the dynamic SQL statement + EXECUTE dynamic_query USING query_user_ids; + + -- Add any post-deletion logic here if needed +END; +$_$; + +ALTER FUNCTION "public"."remove_memories"(query_table_name text, query_user_ids uuid[]) OWNER TO "postgres"; + +CREATE OR REPLACE FUNCTION "public"."search_memories"(query_table_name text, query_user_ids uuid[], query_embedding extensions.vector, query_match_threshold double precision, query_match_count integer, query_unique boolean) RETURNS TABLE(id uuid, user_id uuid, content jsonb, created_at timestamp with time zone, similarity double precision, user_ids uuid[], room_id uuid, embedding extensions.vector) + LANGUAGE "plpgsql" + AS $_$ +DECLARE + query TEXT; +BEGIN + query := format($fmt$ + SELECT + id, + user_id, + content, + created_at, + 1 - (embedding <=> %L) AS similarity, -- Use '<=>' for cosine distance + user_ids, + room_id, + embedding + FROM %I + WHERE (1 - (embedding <=> %L) > %L) + %s -- Condition for user_ids + %s -- Additional condition for 'unique' column + ORDER BY similarity DESC + LIMIT %L + $fmt$, + query_embedding, + query_table_name, + query_embedding, + query_match_threshold, + CASE WHEN query_user_ids IS NOT NULL THEN format(' AND user_ids @> %L', query_user_ids) ELSE '' END, + CASE WHEN query_unique THEN ' AND unique = TRUE' ELSE '' END, -- Add condition based on 'query_unique' + query_match_count + ); + + RETURN QUERY EXECUTE query; +END; +$_$; + +ALTER FUNCTION "public"."search_memories"(query_table_name text, query_user_ids uuid[], query_embedding extensions.vector, query_match_threshold double precision, query_match_count integer, query_unique boolean) OWNER TO "postgres"; + +CREATE OR REPLACE FUNCTION "public"."search_messages"(query_embedding extensions.vector, similarity_threshold double precision, match_count integer, owner_id uuid, chat_id uuid DEFAULT NULL::uuid) RETURNS TABLE(content text, role text, created_at timestamp with time zone) + LANGUAGE "plpgsql" + AS $$ +BEGIN + RETURN QUERY + SELECT + messages.content, + messages.role, + messages.created_at::timestamp with time zone + FROM messages + WHERE + messages.owner = owner_id AND + (chat_id IS NULL OR messages.chat = chat_id) AND + 1 - (messages.embedding <=> query_embedding) > similarity_threshold + ORDER BY + 1 - (messages.embedding <=> query_embedding) DESC, + messages.created_at + LIMIT match_count; +END; +$$; + +ALTER FUNCTION "public"."search_messages"(query_embedding extensions.vector, similarity_threshold double precision, match_count integer, owner_id uuid, chat_id uuid) OWNER TO "postgres"; + +CREATE TABLE IF NOT EXISTS "public"."accounts" ( + "id" uuid NOT NULL, + "created_at" timestamp with time zone DEFAULT (now() AT TIME ZONE 'utc'::text) NOT NULL, + "name" text, + "email" text NOT NULL, + "avatar_url" text, + "register_complete" boolean NOT NULL, + "details" jsonb DEFAULT '{}'::jsonb +); + +ALTER TABLE "public"."accounts" OWNER TO "postgres"; + +CREATE TABLE IF NOT EXISTS "public"."descriptions" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "content" jsonb NOT NULL, + "embedding" extensions.vector NOT NULL, + "user_id" uuid, + "user_ids" uuid[], + "room_id" uuid, + "name" text, + "unique" boolean DEFAULT true NOT NULL +); +ALTER TABLE ONLY "public"."descriptions" ALTER COLUMN "embedding" SET STORAGE EXTENDED; + +ALTER TABLE "public"."descriptions" OWNER TO "postgres"; + +CREATE TABLE IF NOT EXISTS "public"."logs" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "user_id" uuid NOT NULL, + "body" jsonb NOT NULL, + "type" text NOT NULL, + "room_id" uuid NOT NULL, + "user_ids" uuid[] NOT NULL, + "agent_id" uuid NOT NULL +); + +ALTER TABLE "public"."logs" OWNER TO "postgres"; + +CREATE TABLE IF NOT EXISTS "public"."messages" ( + "created_at" timestamp with time zone DEFAULT (now() AT TIME ZONE 'utc'::text) NOT NULL, + "user_id" uuid, + "content" jsonb, + "is_edited" boolean DEFAULT false, + "room_id" uuid, + "updated_at" timestamp with time zone, + "user_ids" uuid[] DEFAULT '{}'::uuid[] NOT NULL, + "id" uuid DEFAULT gen_random_uuid() NOT NULL, + "embedding" extensions.vector, + "unique" boolean DEFAULT true NOT NULL +); +ALTER TABLE ONLY "public"."messages" ALTER COLUMN "embedding" SET STORAGE EXTENDED; + +ALTER TABLE "public"."messages" OWNER TO "postgres"; + +CREATE TABLE IF NOT EXISTS "public"."participants" ( + "created_at" timestamp with time zone DEFAULT (now() AT TIME ZONE 'utc'::text) NOT NULL, + "user_id" uuid, + "room_id" uuid, + "id" uuid DEFAULT gen_random_uuid() NOT NULL, + "last_message_read" uuid +); + +ALTER TABLE "public"."participants" OWNER TO "postgres"; + +CREATE TABLE IF NOT EXISTS "public"."rooms" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp with time zone DEFAULT (now() AT TIME ZONE 'utc'::text) NOT NULL, + "created_by" uuid, + "name" text +); + +ALTER TABLE "public"."rooms" OWNER TO "postgres"; + +CREATE TABLE IF NOT EXISTS "public"."summarizations" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "content" jsonb NOT NULL, + "embedding" extensions.vector NOT NULL, + "user_id" uuid, + "user_ids" uuid[], + "room_id" uuid, + "unique" boolean DEFAULT true NOT NULL +); +ALTER TABLE ONLY "public"."summarizations" ALTER COLUMN "embedding" SET STORAGE EXTENDED; + +ALTER TABLE "public"."summarizations" OWNER TO "postgres"; + +ALTER TABLE ONLY "public"."descriptions" + ADD CONSTRAINT "descriptions_pkey" PRIMARY KEY ("id"); + +ALTER TABLE ONLY "public"."relationships" + ADD CONSTRAINT "friendships_id_key" UNIQUE ("id"); + +ALTER TABLE ONLY "public"."relationships" + ADD CONSTRAINT "friendships_pkey" PRIMARY KEY ("id"); + +ALTER TABLE ONLY "public"."goals" + ADD CONSTRAINT "goals_pkey" PRIMARY KEY ("id"); + +ALTER TABLE ONLY "public"."logs" + ADD CONSTRAINT "logs_pkey" PRIMARY KEY ("id"); + +ALTER TABLE ONLY "public"."messages" + ADD CONSTRAINT "messages_id_key" UNIQUE ("id"); + +ALTER TABLE ONLY "public"."messages" + ADD CONSTRAINT "messages_pkey" PRIMARY KEY ("id"); + +ALTER TABLE ONLY "public"."participants" + ADD CONSTRAINT "participants_id_key" UNIQUE ("id"); + +ALTER TABLE ONLY "public"."participants" + ADD CONSTRAINT "participants_pkey" PRIMARY KEY ("id"); + +ALTER TABLE ONLY "public"."summarizations" + ADD CONSTRAINT "reflections_pkey" PRIMARY KEY ("id"); + +ALTER TABLE ONLY "public"."rooms" + ADD CONSTRAINT "rooms_pkey" PRIMARY KEY ("id"); + +ALTER TABLE ONLY "public"."accounts" + ADD CONSTRAINT "users_email_key" UNIQUE ("email"); + +ALTER TABLE ONLY "public"."accounts" + ADD CONSTRAINT "users_pkey" PRIMARY KEY ("id"); + +CREATE TRIGGER trigger_create_friendship_with_host_agent AFTER INSERT ON public.accounts FOR EACH ROW EXECUTE FUNCTION public.create_friendship_with_host_agent(); + +ALTER TABLE ONLY "public"."descriptions" + ADD CONSTRAINT "descriptions_room_id_fkey" FOREIGN KEY (room_id) REFERENCES public.rooms(id); + +ALTER TABLE ONLY "public"."descriptions" + ADD CONSTRAINT "descriptions_user_id_fkey" FOREIGN KEY (user_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY "public"."messages" + ADD CONSTRAINT "messages_room_id_fkey" FOREIGN KEY (room_id) REFERENCES public.rooms(id); + +ALTER TABLE ONLY "public"."messages" + ADD CONSTRAINT "messages_user_id_fkey" FOREIGN KEY (user_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY "public"."participants" + ADD CONSTRAINT "participants_room_id_fkey" FOREIGN KEY (room_id) REFERENCES public.rooms(id); + +ALTER TABLE ONLY "public"."participants" + ADD CONSTRAINT "participants_user_id_fkey" FOREIGN KEY (user_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY "public"."summarizations" + ADD CONSTRAINT "reflections_room_id_fkey" FOREIGN KEY (room_id) REFERENCES public.rooms(id); + +ALTER TABLE ONLY "public"."summarizations" + ADD CONSTRAINT "reflections_user_id_fkey" FOREIGN KEY (user_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY "public"."relationships" + ADD CONSTRAINT "relationships_room_id_fkey" FOREIGN KEY (room_id) REFERENCES public.rooms(id) ON UPDATE CASCADE ON DELETE CASCADE; + +ALTER TABLE ONLY "public"."relationships" + ADD CONSTRAINT "relationships_user_a_fkey" FOREIGN KEY (user_a) REFERENCES public.accounts(id); + +ALTER TABLE ONLY "public"."relationships" + ADD CONSTRAINT "relationships_user_b_fkey" FOREIGN KEY (user_b) REFERENCES public.accounts(id); + +ALTER TABLE ONLY "public"."relationships" + ADD CONSTRAINT "relationships_user_id_fkey" FOREIGN KEY (user_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY "public"."rooms" + ADD CONSTRAINT "rooms_created_by_fkey" FOREIGN KEY (created_by) REFERENCES public.accounts(id); + +CREATE POLICY "Can select and update all data" ON "public"."accounts" USING ((auth.uid() = id)) WITH CHECK ((auth.uid() = id)); + +CREATE POLICY "Enable for authenticated users only" ON "public"."summarizations" TO authenticated USING ((auth.uid() = ANY (user_ids))) WITH CHECK ((auth.uid() = ANY (user_ids))); + +CREATE POLICY "Enable for users based on user_id" ON "public"."descriptions" TO authenticated USING ((auth.uid() = ANY (user_ids))) WITH CHECK ((auth.uid() = ANY (user_ids))); + +CREATE POLICY "Enable insert for authenticated users only" ON "public"."accounts" FOR INSERT TO authenticated, anon WITH CHECK (true); + +CREATE POLICY "Enable insert for authenticated users only" ON "public"."logs" FOR INSERT TO authenticated, anon WITH CHECK (true); + +CREATE POLICY "Enable insert for authenticated users only" ON "public"."messages" TO authenticated USING (true) WITH CHECK (true); + +CREATE POLICY "Enable insert for authenticated users only" ON "public"."participants" FOR INSERT TO authenticated WITH CHECK (true); + +CREATE POLICY "Enable insert for authenticated users only" ON "public"."relationships" FOR INSERT TO authenticated WITH CHECK (((auth.uid() = user_a) OR (auth.uid() = user_b))); + +CREATE POLICY "Enable insert for authenticated users only" ON "public"."rooms" FOR INSERT WITH CHECK (true); + +CREATE POLICY "Enable insert for self id" ON "public"."participants" USING ((auth.uid() = user_id)) WITH CHECK ((auth.uid() = user_id)); + +CREATE POLICY "Enable read access for all users" ON "public"."accounts" FOR SELECT USING (true); + +CREATE POLICY "Enable read access for all users" ON "public"."rooms" FOR SELECT TO authenticated USING (true); + +CREATE POLICY "Enable read access for own messages" ON "public"."messages" FOR SELECT TO authenticated USING (((auth.uid() = ANY (user_ids)) OR (auth.uid() = user_id))); + +CREATE POLICY "Enable read access for own rooms" ON "public"."participants" FOR SELECT TO authenticated USING ((auth.uid() = user_id)); + +CREATE POLICY "Enable read access for user to their own relationships" ON "public"."relationships" FOR SELECT TO authenticated USING (((auth.uid() = user_a) OR (auth.uid() = user_b))); + +CREATE POLICY "Enable update for users of own id" ON "public"."rooms" FOR UPDATE USING (true) WITH CHECK (true); + +ALTER TABLE "public"."accounts" ENABLE ROW LEVEL SECURITY; + +ALTER TABLE "public"."descriptions" ENABLE ROW LEVEL SECURITY; + +ALTER TABLE "public"."goals" ENABLE ROW LEVEL SECURITY; + +ALTER TABLE "public"."logs" ENABLE ROW LEVEL SECURITY; + +ALTER TABLE "public"."messages" ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "select_own_account" ON "public"."accounts" FOR SELECT USING ((auth.uid() = id)); + +GRANT USAGE ON SCHEMA "public" TO "postgres"; +GRANT USAGE ON SCHEMA "public" TO "anon"; +GRANT USAGE ON SCHEMA "public" TO "authenticated"; +GRANT USAGE ON SCHEMA "public" TO "service_role"; + +GRANT ALL ON FUNCTION "public"."check_similarity_and_insert"(query_table_name text, query_user_id uuid, query_user_ids uuid[], query_content jsonb, query_room_id uuid, query_embedding extensions.vector, similarity_threshold double precision) TO "anon"; +GRANT ALL ON FUNCTION "public"."check_similarity_and_insert"(query_table_name text, query_user_id uuid, query_user_ids uuid[], query_content jsonb, query_room_id uuid, query_embedding extensions.vector, similarity_threshold double precision) TO "authenticated"; +GRANT ALL ON FUNCTION "public"."check_similarity_and_insert"(query_table_name text, query_user_id uuid, query_user_ids uuid[], query_content jsonb, query_room_id uuid, query_embedding extensions.vector, similarity_threshold double precision) TO "service_role"; + +GRANT ALL ON FUNCTION "public"."count_memories"(query_table_name text, query_user_ids uuid[], query_unique boolean) TO "anon"; +GRANT ALL ON FUNCTION "public"."count_memories"(query_table_name text, query_user_ids uuid[], query_unique boolean) TO "authenticated"; +GRANT ALL ON FUNCTION "public"."count_memories"(query_table_name text, query_user_ids uuid[], query_unique boolean) TO "service_role"; + +GRANT ALL ON FUNCTION "public"."create_dm_room"() TO "anon"; +GRANT ALL ON FUNCTION "public"."create_dm_room"() TO "authenticated"; +GRANT ALL ON FUNCTION "public"."create_dm_room"() TO "service_role"; + +GRANT ALL ON FUNCTION "public"."create_friendship_and_room_for_user"(p_new_user_id uuid) TO "anon"; +GRANT ALL ON FUNCTION "public"."create_friendship_and_room_for_user"(p_new_user_id uuid) TO "authenticated"; +GRANT ALL ON FUNCTION "public"."create_friendship_and_room_for_user"(p_new_user_id uuid) TO "service_role"; + +GRANT ALL ON FUNCTION "public"."create_friendship_with_host_agent"() TO "anon"; +GRANT ALL ON FUNCTION "public"."create_friendship_with_host_agent"() TO "authenticated"; +GRANT ALL ON FUNCTION "public"."create_friendship_with_host_agent"() TO "service_role"; + +GRANT ALL ON FUNCTION "public"."delete_room_and_participants_on_friendship_delete"() TO "anon"; +GRANT ALL ON FUNCTION "public"."delete_room_and_participants_on_friendship_delete"() TO "authenticated"; +GRANT ALL ON FUNCTION "public"."delete_room_and_participants_on_friendship_delete"() TO "service_role"; + +GRANT ALL ON TABLE "public"."goals" TO "anon"; +GRANT ALL ON TABLE "public"."goals" TO "authenticated"; +GRANT ALL ON TABLE "public"."goals" TO "service_role"; + +GRANT ALL ON FUNCTION "public"."get_goals_by_user_ids"(query_user_ids uuid[], query_user_id uuid, only_in_progress boolean, row_count integer) TO "anon"; +GRANT ALL ON FUNCTION "public"."get_goals_by_user_ids"(query_user_ids uuid[], query_user_id uuid, only_in_progress boolean, row_count integer) TO "authenticated"; +GRANT ALL ON FUNCTION "public"."get_goals_by_user_ids"(query_user_ids uuid[], query_user_id uuid, only_in_progress boolean, row_count integer) TO "service_role"; + +GRANT ALL ON FUNCTION "public"."get_memories"(query_table_name text, query_user_ids uuid[], query_count integer, query_unique boolean) TO "anon"; +GRANT ALL ON FUNCTION "public"."get_memories"(query_table_name text, query_user_ids uuid[], query_count integer, query_unique boolean) TO "authenticated"; +GRANT ALL ON FUNCTION "public"."get_memories"(query_table_name text, query_user_ids uuid[], query_count integer, query_unique boolean) TO "service_role"; + +GRANT ALL ON FUNCTION "public"."get_message_count"(p_user_id uuid) TO "anon"; +GRANT ALL ON FUNCTION "public"."get_message_count"(p_user_id uuid) TO "authenticated"; +GRANT ALL ON FUNCTION "public"."get_message_count"(p_user_id uuid) TO "service_role"; + +GRANT ALL ON FUNCTION "public"."get_recent_rows_per_user"(query_table_name text, array_of_uuid_arrays uuid[], n_rows_per_user integer, timestamp_column_name text) TO "anon"; +GRANT ALL ON FUNCTION "public"."get_recent_rows_per_user"(query_table_name text, array_of_uuid_arrays uuid[], n_rows_per_user integer, timestamp_column_name text) TO "authenticated"; +GRANT ALL ON FUNCTION "public"."get_recent_rows_per_user"(query_table_name text, array_of_uuid_arrays uuid[], n_rows_per_user integer, timestamp_column_name text) TO "service_role"; + +GRANT ALL ON TABLE "public"."relationships" TO "anon"; +GRANT ALL ON TABLE "public"."relationships" TO "authenticated"; +GRANT ALL ON TABLE "public"."relationships" TO "service_role"; + +GRANT ALL ON FUNCTION "public"."get_relationship"(usera uuid, userb uuid) TO "anon"; +GRANT ALL ON FUNCTION "public"."get_relationship"(usera uuid, userb uuid) TO "authenticated"; +GRANT ALL ON FUNCTION "public"."get_relationship"(usera uuid, userb uuid) TO "service_role"; + +GRANT ALL ON FUNCTION "public"."handle_new_user"() TO "anon"; +GRANT ALL ON FUNCTION "public"."handle_new_user"() TO "authenticated"; +GRANT ALL ON FUNCTION "public"."handle_new_user"() TO "service_role"; + +GRANT ALL ON FUNCTION "public"."is_user_participant_in_room"(p_user_id uuid, p_room_id uuid) TO "anon"; +GRANT ALL ON FUNCTION "public"."is_user_participant_in_room"(p_user_id uuid, p_room_id uuid) TO "authenticated"; +GRANT ALL ON FUNCTION "public"."is_user_participant_in_room"(p_user_id uuid, p_room_id uuid) TO "service_role"; + +GRANT ALL ON FUNCTION "public"."remove_memories"(query_table_name text, query_user_ids uuid[]) TO "anon"; +GRANT ALL ON FUNCTION "public"."remove_memories"(query_table_name text, query_user_ids uuid[]) TO "authenticated"; +GRANT ALL ON FUNCTION "public"."remove_memories"(query_table_name text, query_user_ids uuid[]) TO "service_role"; + +GRANT ALL ON FUNCTION "public"."search_memories"(query_table_name text, query_user_ids uuid[], query_embedding extensions.vector, query_match_threshold double precision, query_match_count integer, query_unique boolean) TO "anon"; +GRANT ALL ON FUNCTION "public"."search_memories"(query_table_name text, query_user_ids uuid[], query_embedding extensions.vector, query_match_threshold double precision, query_match_count integer, query_unique boolean) TO "authenticated"; +GRANT ALL ON FUNCTION "public"."search_memories"(query_table_name text, query_user_ids uuid[], query_embedding extensions.vector, query_match_threshold double precision, query_match_count integer, query_unique boolean) TO "service_role"; + +GRANT ALL ON FUNCTION "public"."search_messages"(query_embedding extensions.vector, similarity_threshold double precision, match_count integer, owner_id uuid, chat_id uuid) TO "anon"; +GRANT ALL ON FUNCTION "public"."search_messages"(query_embedding extensions.vector, similarity_threshold double precision, match_count integer, owner_id uuid, chat_id uuid) TO "authenticated"; +GRANT ALL ON FUNCTION "public"."search_messages"(query_embedding extensions.vector, similarity_threshold double precision, match_count integer, owner_id uuid, chat_id uuid) TO "service_role"; + +GRANT ALL ON TABLE "public"."accounts" TO "anon"; +GRANT ALL ON TABLE "public"."accounts" TO "authenticated"; +GRANT ALL ON TABLE "public"."accounts" TO "service_role"; + +GRANT ALL ON TABLE "public"."descriptions" TO "anon"; +GRANT ALL ON TABLE "public"."descriptions" TO "authenticated"; +GRANT ALL ON TABLE "public"."descriptions" TO "service_role"; + +GRANT ALL ON TABLE "public"."logs" TO "anon"; +GRANT ALL ON TABLE "public"."logs" TO "authenticated"; +GRANT ALL ON TABLE "public"."logs" TO "service_role"; + +GRANT ALL ON TABLE "public"."messages" TO "anon"; +GRANT ALL ON TABLE "public"."messages" TO "authenticated"; +GRANT ALL ON TABLE "public"."messages" TO "service_role"; + +GRANT ALL ON TABLE "public"."participants" TO "anon"; +GRANT ALL ON TABLE "public"."participants" TO "authenticated"; +GRANT ALL ON TABLE "public"."participants" TO "service_role"; + +GRANT ALL ON TABLE "public"."rooms" TO "anon"; +GRANT ALL ON TABLE "public"."rooms" TO "authenticated"; +GRANT ALL ON TABLE "public"."rooms" TO "service_role"; + +GRANT ALL ON TABLE "public"."summarizations" TO "anon"; +GRANT ALL ON TABLE "public"."summarizations" TO "authenticated"; +GRANT ALL ON TABLE "public"."summarizations" TO "service_role"; + +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "postgres"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "anon"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "authenticated"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "service_role"; + +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "postgres"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "anon"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "authenticated"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "service_role"; + +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "postgres"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "anon"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "authenticated"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "service_role"; + +RESET ALL; diff --git a/src/supabase/types.ts b/src/supabase/types.ts new file mode 100644 index 0000000..ea161b7 --- /dev/null +++ b/src/supabase/types.ts @@ -0,0 +1,840 @@ +export type Json = + | string + | number + | boolean + | null + | { [key: string]: Json | undefined } + | Json[]; + +export type Database = { + graphql_public: { + Tables: { + [_ in never]: never; + }; + Views: { + [_ in never]: never; + }; + Functions: { + graphql: { + Args: { + operationName?: string; + query?: string; + variables?: Json; + extensions?: Json; + }; + Returns: Json; + }; + }; + Enums: { + [_ in never]: never; + }; + CompositeTypes: { + [_ in never]: never; + }; + }; + public: { + Tables: { + accounts: { + Row: { + avatar_url: string | null; + created_at: string; + details: Json | null; + email: string; + id: string; + name: string | null; + register_complete: boolean; + }; + Insert: { + avatar_url?: string | null; + created_at?: string; + details?: Json | null; + email: string; + id: string; + name?: string | null; + register_complete: boolean; + }; + Update: { + avatar_url?: string | null; + created_at?: string; + details?: Json | null; + email?: string; + id?: string; + name?: string | null; + register_complete?: boolean; + }; + Relationships: []; + }; + descriptions: { + Row: { + content: Json; + created_at: string; + embedding: string; + id: string; + name: string | null; + room_id: string | null; + unique: boolean; + user_id: string | null; + user_ids: string[] | null; + }; + Insert: { + content: Json; + created_at?: string; + embedding: string; + id?: string; + name?: string | null; + room_id?: string | null; + unique?: boolean; + user_id?: string | null; + user_ids?: string[] | null; + }; + Update: { + content?: Json; + created_at?: string; + embedding?: string; + id?: string; + name?: string | null; + room_id?: string | null; + unique?: boolean; + user_id?: string | null; + user_ids?: string[] | null; + }; + Relationships: [ + { + foreignKeyName: "descriptions_room_id_fkey"; + columns: ["room_id"]; + isOneToOne: false; + referencedRelation: "rooms"; + referencedColumns: ["id"]; + }, + { + foreignKeyName: "descriptions_user_id_fkey"; + columns: ["user_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; + }, + ]; + }; + goals: { + Row: { + created_at: string; + description: string | null; + id: string; + name: string | null; + objectives: Json[]; + status: string | null; + user_id: string | null; + user_ids: string[]; + }; + Insert: { + created_at?: string; + description?: string | null; + id?: string; + name?: string | null; + objectives?: Json[]; + status?: string | null; + user_id?: string | null; + user_ids?: string[]; + }; + Update: { + created_at?: string; + description?: string | null; + id?: string; + name?: string | null; + objectives?: Json[]; + status?: string | null; + user_id?: string | null; + user_ids?: string[]; + }; + Relationships: []; + }; + logs: { + Row: { + agent_id: string; + body: Json; + created_at: string; + id: string; + room_id: string; + type: string; + user_id: string; + user_ids: string[]; + }; + Insert: { + agent_id: string; + body: Json; + created_at?: string; + id?: string; + room_id: string; + type: string; + user_id: string; + user_ids: string[]; + }; + Update: { + agent_id?: string; + body?: Json; + created_at?: string; + id?: string; + room_id?: string; + type?: string; + user_id?: string; + user_ids?: string[]; + }; + Relationships: []; + }; + messages: { + Row: { + content: Json | null; + created_at: string; + embedding: string | null; + id: string; + is_edited: boolean | null; + room_id: string | null; + unique: boolean; + updated_at: string | null; + user_id: string | null; + user_ids: string[]; + }; + Insert: { + content?: Json | null; + created_at?: string; + embedding?: string | null; + id?: string; + is_edited?: boolean | null; + room_id?: string | null; + unique?: boolean; + updated_at?: string | null; + user_id?: string | null; + user_ids?: string[]; + }; + Update: { + content?: Json | null; + created_at?: string; + embedding?: string | null; + id?: string; + is_edited?: boolean | null; + room_id?: string | null; + unique?: boolean; + updated_at?: string | null; + user_id?: string | null; + user_ids?: string[]; + }; + Relationships: [ + { + foreignKeyName: "messages_room_id_fkey"; + columns: ["room_id"]; + isOneToOne: false; + referencedRelation: "rooms"; + referencedColumns: ["id"]; + }, + { + foreignKeyName: "messages_user_id_fkey"; + columns: ["user_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; + }, + ]; + }; + participants: { + Row: { + created_at: string; + id: string; + last_message_read: string | null; + room_id: string | null; + user_id: string | null; + }; + Insert: { + created_at?: string; + id?: string; + last_message_read?: string | null; + room_id?: string | null; + user_id?: string | null; + }; + Update: { + created_at?: string; + id?: string; + last_message_read?: string | null; + room_id?: string | null; + user_id?: string | null; + }; + Relationships: [ + { + foreignKeyName: "participants_room_id_fkey"; + columns: ["room_id"]; + isOneToOne: false; + referencedRelation: "rooms"; + referencedColumns: ["id"]; + }, + { + foreignKeyName: "participants_user_id_fkey"; + columns: ["user_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; + }, + ]; + }; + relationships: { + Row: { + created_at: string; + id: string; + room_id: string | null; + status: string | null; + user_a: string | null; + user_b: string | null; + user_id: string; + }; + Insert: { + created_at?: string; + id?: string; + room_id?: string | null; + status?: string | null; + user_a?: string | null; + user_b?: string | null; + user_id: string; + }; + Update: { + created_at?: string; + id?: string; + room_id?: string | null; + status?: string | null; + user_a?: string | null; + user_b?: string | null; + user_id?: string; + }; + Relationships: [ + { + foreignKeyName: "relationships_room_id_fkey"; + columns: ["room_id"]; + isOneToOne: false; + referencedRelation: "rooms"; + referencedColumns: ["id"]; + }, + { + foreignKeyName: "relationships_user_a_fkey"; + columns: ["user_a"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; + }, + { + foreignKeyName: "relationships_user_b_fkey"; + columns: ["user_b"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; + }, + { + foreignKeyName: "relationships_user_id_fkey"; + columns: ["user_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; + }, + ]; + }; + rooms: { + Row: { + created_at: string; + created_by: string | null; + id: string; + name: string | null; + }; + Insert: { + created_at?: string; + created_by?: string | null; + id?: string; + name?: string | null; + }; + Update: { + created_at?: string; + created_by?: string | null; + id?: string; + name?: string | null; + }; + Relationships: [ + { + foreignKeyName: "rooms_created_by_fkey"; + columns: ["created_by"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; + }, + ]; + }; + summarizations: { + Row: { + content: Json; + created_at: string; + embedding: string; + id: string; + room_id: string | null; + unique: boolean; + user_id: string | null; + user_ids: string[] | null; + }; + Insert: { + content: Json; + created_at?: string; + embedding: string; + id?: string; + room_id?: string | null; + unique?: boolean; + user_id?: string | null; + user_ids?: string[] | null; + }; + Update: { + content?: Json; + created_at?: string; + embedding?: string; + id?: string; + room_id?: string | null; + unique?: boolean; + user_id?: string | null; + user_ids?: string[] | null; + }; + Relationships: [ + { + foreignKeyName: "reflections_room_id_fkey"; + columns: ["room_id"]; + isOneToOne: false; + referencedRelation: "rooms"; + referencedColumns: ["id"]; + }, + { + foreignKeyName: "reflections_user_id_fkey"; + columns: ["user_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; + }, + ]; + }; + }; + Views: { + [_ in never]: never; + }; + Functions: { + check_similarity_and_insert: { + Args: { + query_table_name: string; + query_user_id: string; + query_user_ids: string[]; + query_content: Json; + query_room_id: string; + query_embedding: string; + similarity_threshold: number; + }; + Returns: undefined; + }; + count_memories: { + Args: { + query_table_name: string; + query_user_ids: string[]; + query_unique?: boolean; + }; + Returns: number; + }; + create_friendship_and_room_for_user: { + Args: { + p_new_user_id: string; + }; + Returns: undefined; + }; + get_goals_by_user_ids: { + Args: { + query_user_ids: string[]; + query_user_id?: string; + only_in_progress?: boolean; + row_count?: number; + }; + Returns: { + created_at: string; + description: string | null; + id: string; + name: string | null; + objectives: Json[]; + status: string | null; + user_id: string | null; + user_ids: string[]; + }[]; + }; + get_memories: { + Args: { + query_table_name: string; + query_user_ids: string[]; + query_count: number; + query_unique?: boolean; + }; + Returns: { + id: string; + user_id: string; + content: Json; + created_at: string; + user_ids: string[]; + room_id: string; + embedding: string; + }[]; + }; + get_message_count: { + Args: { + p_user_id: string; + }; + Returns: { + room_id: string; + unread_messages_count: number; + }[]; + }; + get_recent_rows_per_user: { + Args: { + query_table_name: string; + array_of_uuid_arrays: string[]; + n_rows_per_user: number; + timestamp_column_name: string; + }; + Returns: { + user_id: string; + content: Json; + timestamp_column: string; + }[]; + }; + get_relationship: { + Args: { + usera: string; + userb: string; + }; + Returns: { + created_at: string; + id: string; + room_id: string | null; + status: string | null; + user_a: string | null; + user_b: string | null; + user_id: string; + }[]; + }; + is_user_participant_in_room: { + Args: { + p_user_id: string; + p_room_id: string; + }; + Returns: boolean; + }; + remove_memories: { + Args: { + query_table_name: string; + query_user_ids: string[]; + }; + Returns: undefined; + }; + search_memories: { + Args: { + query_table_name: string; + query_user_ids: string[]; + query_embedding: string; + query_match_threshold: number; + query_match_count: number; + query_unique: boolean; + }; + Returns: { + id: string; + user_id: string; + content: Json; + created_at: string; + similarity: number; + user_ids: string[]; + room_id: string; + embedding: string; + }[]; + }; + search_messages: { + Args: { + query_embedding: string; + similarity_threshold: number; + match_count: number; + owner_id: string; + chat_id?: string; + }; + Returns: { + content: string; + role: string; + created_at: string; + }[]; + }; + }; + Enums: { + pricing_plan_interval: "day" | "week" | "month" | "year"; + pricing_type: "one_time" | "recurring"; + subscription_status: + | "trialing" + | "active" + | "canceled" + | "incomplete" + | "incomplete_expired" + | "past_due" + | "unpaid"; + }; + CompositeTypes: { + [_ in never]: never; + }; + }; + storage: { + Tables: { + buckets: { + Row: { + allowed_mime_types: string[] | null; + avif_autodetection: boolean | null; + created_at: string | null; + file_size_limit: number | null; + id: string; + name: string; + owner: string | null; + owner_id: string | null; + public: boolean | null; + updated_at: string | null; + }; + Insert: { + allowed_mime_types?: string[] | null; + avif_autodetection?: boolean | null; + created_at?: string | null; + file_size_limit?: number | null; + id: string; + name: string; + owner?: string | null; + owner_id?: string | null; + public?: boolean | null; + updated_at?: string | null; + }; + Update: { + allowed_mime_types?: string[] | null; + avif_autodetection?: boolean | null; + created_at?: string | null; + file_size_limit?: number | null; + id?: string; + name?: string; + owner?: string | null; + owner_id?: string | null; + public?: boolean | null; + updated_at?: string | null; + }; + Relationships: []; + }; + migrations: { + Row: { + executed_at: string | null; + hash: string; + id: number; + name: string; + }; + Insert: { + executed_at?: string | null; + hash: string; + id: number; + name: string; + }; + Update: { + executed_at?: string | null; + hash?: string; + id?: number; + name?: string; + }; + Relationships: []; + }; + objects: { + Row: { + bucket_id: string | null; + created_at: string | null; + id: string; + last_accessed_at: string | null; + metadata: Json | null; + name: string | null; + owner: string | null; + owner_id: string | null; + path_tokens: string[] | null; + updated_at: string | null; + version: string | null; + }; + Insert: { + bucket_id?: string | null; + created_at?: string | null; + id?: string; + last_accessed_at?: string | null; + metadata?: Json | null; + name?: string | null; + owner?: string | null; + owner_id?: string | null; + path_tokens?: string[] | null; + updated_at?: string | null; + version?: string | null; + }; + Update: { + bucket_id?: string | null; + created_at?: string | null; + id?: string; + last_accessed_at?: string | null; + metadata?: Json | null; + name?: string | null; + owner?: string | null; + owner_id?: string | null; + path_tokens?: string[] | null; + updated_at?: string | null; + version?: string | null; + }; + Relationships: [ + { + foreignKeyName: "objects_bucketId_fkey"; + columns: ["bucket_id"]; + isOneToOne: false; + referencedRelation: "buckets"; + referencedColumns: ["id"]; + }, + ]; + }; + }; + Views: { + [_ in never]: never; + }; + Functions: { + can_insert_object: { + Args: { + bucketid: string; + name: string; + owner: string; + metadata: Json; + }; + Returns: undefined; + }; + extension: { + Args: { + name: string; + }; + Returns: string; + }; + filename: { + Args: { + name: string; + }; + Returns: string; + }; + foldername: { + Args: { + name: string; + }; + Returns: unknown; + }; + get_size_by_bucket: { + Args: Record; + Returns: { + size: number; + bucket_id: string; + }[]; + }; + search: { + Args: { + prefix: string; + bucketname: string; + limits?: number; + levels?: number; + offsets?: number; + search?: string; + sortcolumn?: string; + sortorder?: string; + }; + Returns: { + name: string; + id: string; + updated_at: string; + created_at: string; + last_accessed_at: string; + metadata: Json; + }[]; + }; + }; + Enums: { + [_ in never]: never; + }; + CompositeTypes: { + [_ in never]: never; + }; + }; +}; + +export type Tables< + PublicTableNameOrOptions extends + | keyof (Database["public"]["Tables"] & Database["public"]["Views"]) + | { schema: keyof Database }, + TableName extends PublicTableNameOrOptions extends { schema: keyof Database } + ? keyof (Database[PublicTableNameOrOptions["schema"]]["Tables"] & + Database[PublicTableNameOrOptions["schema"]]["Views"]) + : never = never, +> = PublicTableNameOrOptions extends { schema: keyof Database } + ? (Database[PublicTableNameOrOptions["schema"]]["Tables"] & + Database[PublicTableNameOrOptions["schema"]]["Views"])[TableName] extends { + Row: infer R; + } + ? R + : never + : PublicTableNameOrOptions extends keyof (Database["public"]["Tables"] & + Database["public"]["Views"]) + ? (Database["public"]["Tables"] & + Database["public"]["Views"])[PublicTableNameOrOptions] extends { + Row: infer R; + } + ? R + : never + : never; + +export type TablesInsert< + PublicTableNameOrOptions extends + | keyof Database["public"]["Tables"] + | { schema: keyof Database }, + TableName extends PublicTableNameOrOptions extends { schema: keyof Database } + ? keyof Database[PublicTableNameOrOptions["schema"]]["Tables"] + : never = never, +> = PublicTableNameOrOptions extends { schema: keyof Database } + ? Database[PublicTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Insert: infer I; + } + ? I + : never + : PublicTableNameOrOptions extends keyof Database["public"]["Tables"] + ? Database["public"]["Tables"][PublicTableNameOrOptions] extends { + Insert: infer I; + } + ? I + : never + : never; + +export type TablesUpdate< + PublicTableNameOrOptions extends + | keyof Database["public"]["Tables"] + | { schema: keyof Database }, + TableName extends PublicTableNameOrOptions extends { schema: keyof Database } + ? keyof Database[PublicTableNameOrOptions["schema"]]["Tables"] + : never = never, +> = PublicTableNameOrOptions extends { schema: keyof Database } + ? Database[PublicTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Update: infer U; + } + ? U + : never + : PublicTableNameOrOptions extends keyof Database["public"]["Tables"] + ? Database["public"]["Tables"][PublicTableNameOrOptions] extends { + Update: infer U; + } + ? U + : never + : never; + +export type Enums< + PublicEnumNameOrOptions extends + | keyof Database["public"]["Enums"] + | { schema: keyof Database }, + EnumName extends PublicEnumNameOrOptions extends { schema: keyof Database } + ? keyof Database[PublicEnumNameOrOptions["schema"]]["Enums"] + : never = never, +> = PublicEnumNameOrOptions extends { schema: keyof Database } + ? Database[PublicEnumNameOrOptions["schema"]]["Enums"][EnumName] + : PublicEnumNameOrOptions extends keyof Database["public"]["Enums"] + ? Database["public"]["Enums"][PublicEnumNameOrOptions] + : never; diff --git a/src/test/createRuntime.ts b/src/test/createRuntime.ts index 13c5f3b..feec5d8 100644 --- a/src/test/createRuntime.ts +++ b/src/test/createRuntime.ts @@ -6,11 +6,19 @@ import { SUPABASE_URL, SUPABASE_ANON_KEY, } from "./constants"; +import { Action, Evaluator } from "../lib/types"; -export async function createRuntime( - env: Record | NodeJS.ProcessEnv = process.env, - recentMessageCount = 32, -) { +export async function createRuntime({ + env, + recentMessageCount, + evaluators = [], + actions = [], +}: { + env?: Record | NodeJS.ProcessEnv; + recentMessageCount?: number; + evaluators?: Evaluator[]; + actions?: Action[]; +}) { const supabase = createClient(SUPABASE_URL!, SUPABASE_ANON_KEY!); const { @@ -24,12 +32,16 @@ export async function createRuntime( throw new Error("Session not found"); } + console.log('*** creating runtime with action', actions, 'and evaluators', evaluators) + const runtime = new BgentRuntime({ debugMode: false, serverUrl: "https://api.openai.com/v1", supabase, recentMessageCount, - token: env.OPENAI_API_KEY!, + token: env!.OPENAI_API_KEY!, + actions: actions ?? [], + evaluators: evaluators ?? [], }); return { user, session, runtime }; diff --git a/src/test/testAction.ts b/src/test/testAction.ts new file mode 100644 index 0000000..52d19c0 --- /dev/null +++ b/src/test/testAction.ts @@ -0,0 +1,74 @@ +import { type BgentRuntime } from "../lib/runtime"; +import { type Action, type Message } from "../lib/types"; + +export const TEST_ACTION = { + name: "TEST_ACTION", + validate: async ( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _runtime: BgentRuntime, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _message: Message, + ) => { + return true; + }, + description: "This is a test action, for use in testing.", + handler: async ( + runtime: BgentRuntime, + message: Message, + ): Promise => { + if (runtime.debugMode) { + console.log("Ignored message: ", message.content); + } + return true; + }, + condition: + "We are in a testing environment and want to test the action handler.", + examples: [ + [ + { + user: "{{user1}}", + content: + "Please respond with the message 'testing 123' and the action TEST_ACTION", + action: "TEST_ACTION", + }, + { + user: "{{user2}}", + content: "testing 123", + action: "TEST_ACTION", + }, + ], + ], +} as Action; + +export const TEST_ACTION_FAIL = { + name: "TEST_ACTION_FAIL", + validate: async ( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _runtime: BgentRuntime, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _message: Message, + ) => { + return false; + }, + description: "This is a test action, for use in testing.", + handler: async ( + runtime: BgentRuntime, + message: Message, + ): Promise => { + if (runtime.debugMode) { + console.log("Ignored message: ", message.content); + } + return false; + }, + condition: + "We are in a testing environment and want to test the action handler failing.", + examples: [ + [ + { + user: "{{user1}}", + content: "Testing failure", + action: "TEST_ACTIONFALSE", + }, + ], + ], +} as Action; diff --git a/src/test/testEvaluator.ts b/src/test/testEvaluator.ts new file mode 100644 index 0000000..d12ef91 --- /dev/null +++ b/src/test/testEvaluator.ts @@ -0,0 +1,64 @@ +import { type BgentRuntime } from "../lib/runtime"; +import { Evaluator, type Message, type State } from "../lib/types"; + +async function handler(runtime: BgentRuntime, message: Message) { + const state = (await runtime.composeState(message)) as State; + return state; +} + +export const TEST_EVALUATOR = { + name: "TEST_EVALUATOR", + validate: async ( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _runtime: BgentRuntime, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _message: Message, + ): Promise => { + return await Promise.resolve(true); + }, + description: "Test evaluator.", + condition: "When we are evualating whether tests work.", + handler, + examples: [ + { + context: "Testing, testing, 123 123", + messages: [ + { + user: "{{user1}}", + content: "Testing, testing, 123 123", + action: "TEST_EVALUATOR", + }, + ], + outcome: "There is an outcome.", + }, + ], +} as Evaluator; + +export const TEST_EVALUATOR_FAIL = { + name: "TEST_EVALUATOR_FAIL", + validate: async ( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _runtime: BgentRuntime, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _message: Message, + ): Promise => { + return await Promise.resolve(false); + }, + description: "Test failure of the evaluator and validation.", + condition: "When we are evualating whether tests work.", + handler, + examples: [ + { + context: "Testing, testing, 123 123", + messages: [ + { + user: "{{user1}}", + content: "Testing, testing, 123 123", + action: "TEST_EVALUATOR_FAIL", + }, + ], + outcome: "Things have been tested to have maybe gone wrong.", + }, + ], +} as Evaluator; + diff --git a/wrangler.toml b/wrangler.toml index dec3915..9fbdbb4 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -1,4 +1,4 @@ -name = "cojourney" +name = "bgent" main = "src/agents/index.ts" compatibility_date = "2023-11-21" node_compat = true