Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sweep: Add a new function in agent.ts to slow down conversations #2

Open
1 task done
ykhli opened this issue Aug 16, 2023 · 1 comment · May be fixed by #4
Open
1 task done

Sweep: Add a new function in agent.ts to slow down conversations #2

ykhli opened this issue Aug 16, 2023 · 1 comment · May be fixed by #4
Labels
sweep Assigns Sweep to an issue or pull request.

Comments

@ykhli
Copy link
Owner

ykhli commented Aug 16, 2023

Checklist
  • convex/agent.ts
  • At the top of the file, import the 'sleep' function from the 'utils' file in the 'lib' directory. If it doesn't exist, create it. The function should use the setTimeout function to create a promise that resolves after a specified number of milliseconds. The code should look like this: "import { sleep } from './lib/utils';"
    • In the 'handleAgentInteraction' function, find the part of the code where the messages are being sent in the conversation. This should be inside the while loop that continues until the conversation ends.
    • After each message is sent, call the sleep function with the number of milliseconds you want to delay. The number of milliseconds can be a constant defined at the top of the file. The code should look like this: "await sleep(CONVERSATION_DELAY);"
@sweep-ai sweep-ai bot added the sweep Assigns Sweep to an issue or pull request. label Aug 16, 2023
@sweep-ai
Copy link
Contributor

sweep-ai bot commented Aug 16, 2023

Here's the PR! #4.

⚡ Sweep Free Trial: I used GPT-4 to create this ticket. You have 4 GPT-4 tickets left for the month and 1 for the day. For more GPT-4 tickets, visit our payment portal. To retrigger Sweep edit the issue.


Step 1: 🔍 Code Search

I found the following snippets in your repository. I will now analyze these snippets and come up with a plan.

Some code snippets I looked at (click to expand). If some file is missing from here, you can mention the path in the ticket description.

cat-town/convex/agent.ts

Lines 1 to 302 in 08b994e

// Future: can use node
// 'use node';
// ^ This tells Convex to run this in a `node` environment.
// Read more: https://docs.convex.dev/functions/runtimes
import { v } from 'convex/values';
import { internal } from './_generated/api';
import { Id } from './_generated/dataModel';
import { ActionCtx, internalAction } from './_generated/server';
import { MemoryDB } from './lib/memory';
import { Message, Player } from './schema';
import {
chatHistoryFromMessages,
decideWhoSpeaksNext,
converse,
startConversation,
walkAway,
} from './conversation';
import { getNearbyPlayers } from './lib/physics';
import { CONVERSATION_TIME_LIMIT } from './config';
export const runAgentBatch = internalAction({
args: {
playerIds: v.array(v.id('players')),
noSchedule: v.optional(v.boolean()),
},
handler: async (ctx, { playerIds, noSchedule }) => {
const memory = MemoryDB(ctx);
// TODO: single-flight done & action API to avoid write contention.
const done: DoneFn = handleDone(ctx, noSchedule);
// Get the current state of the world
const { players } = await ctx.runQuery(internal.journal.getSnapshot, { playerIds });
// Segment users by location
const { groups, solos } = divideIntoGroups(players);
// Run a conversation for each group.
const groupPromises = groups.map(async (group) => {
const finished = new Set<Id<'agents'>>();
try {
await handleAgentInteraction(ctx, group, memory, (agentId, activity) => {
if (agentId) finished.add(agentId);
return done(agentId, activity);
});
} catch (e) {
console.error(
'group failed, going for a walk: ',
group.map((p) => p.agentId),
);
for (const player of group) {
if (player.agentId && !finished.has(player.agentId)) {
await done(player.agentId, { type: 'walk', ignore: group.map((p) => p.id) });
}
}
throw e;
}
});
// For those not in a group, run the solo agent loop.
const soloPromises = solos.map(async (player) => {
try {
if (player.agentId) {
await handleAgentSolo(ctx, player, memory, done);
}
} catch (e) {
console.error('agent failed, going for a walk: ', player.agentId);
await done(player.agentId!, { type: 'walk', ignore: [] });
throw e;
}
});
// Make a structure that resolves when the agent yields.
// It should fail to do any actions if the agent has already yielded.
const start = Date.now();
// While testing if you want failures to show up more loudly, use this instead:
await Promise.all([...groupPromises, ...soloPromises]);
// Otherwise, this will allow each group / solo to complete:
// const results = await Promise.allSettled([...groupPromises, ...soloPromises]);
// for (const result of results) {
// if (result.status === 'rejected') {
// console.error(result.reason, playerIds);
// }
// }
console.debug(
`agent batch (${groups.length}g ${solos.length}s) finished: ${Date.now() - start}ms`,
);
},
});
function divideIntoGroups(players: Player[]) {
const playerById = new Map(players.map((p) => [p.id, p]));
const groups: Player[][] = [];
const solos: Player[] = [];
while (playerById.size > 0) {
const player = playerById.values().next().value;
playerById.delete(player.id);
const nearbyPlayers = getNearbyPlayers(player.motion, [...playerById.values()]);
if (nearbyPlayers.length > 0) {
// If you only want to do 1:1 conversations, use this:
// groups.push([player, nearbyPlayers[0]]);
// playerById.delete(nearbyPlayers[0].id);
// otherwise, do more than 1:1 conversations by adding them all:
groups.push([player, ...nearbyPlayers]);
for (const nearbyPlayer of nearbyPlayers) {
playerById.delete(nearbyPlayer.id);
}
} else {
solos.push(player);
}
}
return { groups, solos };
}
async function handleAgentSolo(ctx: ActionCtx, player: Player, memory: MemoryDB, done: DoneFn) {
// console.debug('handleAgentSolo: ', player.name, player.id);
// Handle new observations: it can look at the agent's lastWakeTs for a delta.
// Calculate scores
// Run reflection on memories once in a while
await memory.reflectOnMemories(player.id, player.name);
// Future: Store observations about seeing players in conversation
// might include new observations -> add to memory with openai embeddings
// Later: handle object ownership?
// Based on plan and observations, determine next action:
// if so, add new memory for new plan, and return new action
const walk = player.motion.type === 'stopped' || player.motion.targetEndTs < Date.now();
// Ignore everyone we last said something to.
const ignore =
player.motion.type === 'walking' ? player.motion.ignore : player.lastChat?.message.to ?? [];
await done(player.agentId, { type: walk ? 'walk' : 'continue', ignore });
}
export async function handleAgentInteraction(
ctx: ActionCtx,
players: Player[],
memory: MemoryDB,
done: DoneFn,
) {
// TODO: pick a better conversation starter
const leader = players[0];
for (const player of players) {
const imWalkingHere =
player.motion.type === 'walking' && player.motion.targetEndTs > Date.now();
// Get players to walk together and face each other
if (player.agentId) {
if (player === leader) {
if (imWalkingHere) {
await ctx.runMutation(internal.journal.stop, {
playerId: player.id,
});
}
} else {
await ctx.runMutation(internal.journal.walk, {
agentId: player.agentId,
target: leader.id,
ignore: players.map((p) => p.id),
});
// TODO: collect collisions and pass them into the engine to wake up
// other players to avoid these ones in conversation.
}
}
}
const conversationId = await ctx.runMutation(internal.journal.makeConversation, {
playerId: leader.id,
audience: players.slice(1).map((p) => p.id),
});
const playerById = new Map(players.map((p) => [p.id, p]));
const relations = await ctx.runQuery(internal.journal.getRelationships, {
playerIds: players.map((p) => p.id),
});
const relationshipsByPlayerId = new Map(
relations.map(({ playerId, relations }) => [
playerId,
relations.map((r) => ({ ...playerById.get(playerId)!, relationship: r.relationship })),
]),
);
const messages: Message[] = [];
// TODO: real logic. this just sends one message each!
const endAfterTs = Date.now() + CONVERSATION_TIME_LIMIT;
// Choose who should speak next:
let endConversation = false;
let lastSpeakerId = leader.id;
let remainingPlayers = players;
while (!endConversation) {
// leader speaks first
const chatHistory = chatHistoryFromMessages(messages);
const speaker =
messages.length === 0
? leader
: await decideWhoSpeaksNext(
remainingPlayers.filter((p) => p.id !== lastSpeakerId),
chatHistory,
);
lastSpeakerId = speaker.id;
const audiencePlayers = players.filter((p) => p.id !== speaker.id);
const audience = players.filter((p) => p.id !== speaker.id).map((p) => p.id);
const shouldWalkAway = audience.length === 0 || (await walkAway(chatHistory, speaker));
// Decide if we keep talking.
if (shouldWalkAway || Date.now() > endAfterTs) {
// It's to chatty here, let's go somewhere else.
await ctx.runMutation(internal.journal.leaveConversation, {
playerId: speaker.id,
audience,
conversationId,
});
// Update remaining players
remainingPlayers = remainingPlayers.filter((p) => p.id !== speaker.id);
// End the interaction if there's no one left to talk to.
endConversation = audience.length === 0;
// TODO: remove this player from the audience list
break;
}
// TODO - playerRelations is not used today because of https://github.com/a16z-infra/ai-town/issues/56
const playerRelations = relationshipsByPlayerId.get(speaker.id) ?? [];
let playerCompletion;
if (messages.length === 0) {
playerCompletion = await startConversation(ctx, audiencePlayers, memory, speaker);
} else {
// TODO: stream the response and write to the mutation for every sentence.
playerCompletion = await converse(ctx, chatHistory, speaker, audiencePlayers, memory);
}
const message = await ctx.runMutation(internal.journal.talk, {
playerId: speaker.id,
audience,
content: playerCompletion.content,
relatedMemoryIds: playerCompletion.memoryIds,
conversationId,
});
if (message) {
messages.push(message);
}
}
if (messages.length > 0) {
for (const player of players) {
await memory.rememberConversation(player.name, player.id, player.identity, conversationId);
await done(player.agentId, { type: 'walk', ignore: players.map((p) => p.id) });
}
}
}
type DoneFn = (
agentId: Id<'agents'> | undefined,
activity:
| { type: 'walk'; ignore: Id<'players'>[] }
| { type: 'continue'; ignore: Id<'players'>[] },
) => Promise<void>;
function handleDone(ctx: ActionCtx, noSchedule?: boolean): DoneFn {
const doIt: DoneFn = async (agentId, activity) => {
// console.debug('handleDone: ', agentId, activity);
if (!agentId) return;
let walkResult;
switch (activity.type) {
case 'walk':
walkResult = await ctx.runMutation(internal.journal.walk, {
agentId,
ignore: activity.ignore,
});
break;
case 'continue':
walkResult = await ctx.runQuery(internal.journal.nextCollision, {
agentId,
ignore: activity.ignore,
});
break;
default:
const _exhaustiveCheck: never = activity;
throw new Error(`Unhandled activity: ${JSON.stringify(activity)}`);
}
await ctx.runMutation(internal.engine.agentDone, {
agentId,
otherAgentIds: walkResult.nextCollision?.agentIds,
wakeTs: walkResult.nextCollision?.ts ?? walkResult.targetEndTs,
noSchedule,
});
};
// Simple serialization: only one agent finishes at a time.
let queue = new Set<Promise<unknown>>();
return async (agentId, activity) => {
let unlock;
const wait = new Promise((resolve) => (unlock = resolve));
const toAwait = [...queue];
queue.add(wait);
try {
await Promise.allSettled(toAwait);
await doIt(agentId, activity);
} finally {
unlock!();
queue.delete(wait);
}
};
}

cat-town/convex/testing.ts

Lines 138 to 255 in 08b994e

},
});
export const debugPlayerIdSnapshot = internalQuery({
args: { playerId: v.id('players') },
handler: async (ctx, args) => {
return getPlayer(ctx.db, (await ctx.db.get(args.playerId))!);
},
});
export const listMessages = internalQuery({
args: {},
handler: async (ctx, args) => {
const world = await ctx.db.query('worlds').order('desc').first();
if (!world) return [];
const players = await getAllPlayers(ctx.db, world._id);
const playerIds = players.map((p) => p._id);
const messageEntries = await asyncMap(
playerIds,
(playerId) =>
ctx.db
.query('journal')
.withIndex('by_playerId_type', (q) =>
q.eq('playerId', playerId as any).eq('data.type', 'talking'),
)
.collect() as Promise<EntryOfType<'talking'>[]>,
);
return (
await asyncMap(
messageEntries.flatMap((a) => a),
clientMessageMapper(ctx.db),
)
).sort((a, b) => a.ts - b.ts);
},
});
export const setThinking = internalMutation({
args: { playerIds: v.array(v.id('players')) },
handler: async (ctx, args) => {
const players = pruneNull(await asyncMap(args.playerIds, ctx.db.get));
for (const player of players) {
await ctx.db.patch(player.agentId!, { thinking: true });
}
},
});
export const runAgentLoopClear = internalAction({
args: {
numberOfLoops: v.optional(v.number()),
},
handler: async (ctx, args) => {
await ctx.runAction(internal.init.resetFrozen);
await runAgentLoop(ctx, args);
},
});
export const runAgentLoop = internalAction({
args: {
numberOfLoops: v.optional(v.number()),
},
handler: async (ctx, args) => {
console.log('Looping', args.numberOfLoops || 100);
const { players, world } = await ctx.runQuery(internal.testing.getDebugPlayers);
const playerIds = players.map((p) => p.id);
let index = args.numberOfLoops || 100;
let randomX: number[] = [];
let displacement = 25;
for (let i = 0; i < playerIds.length; i++) {
randomX.push(displacement * i);
}
while (index-- != 0) {
await ctx.runMutation(internal.testing.setThinking, { playerIds });
await ctx.runAction(internal.agent.runAgentBatch, { playerIds, noSchedule: true });
}
},
});
// For making conversations happen without walking around, clear before conversation start.
export const runConversationClear = internalAction({
args: { maxMessages: v.optional(v.number()) },
handler: async (ctx, args) => {
await ctx.runAction(internal.init.resetFrozen);
await runConversation(ctx, args);
},
});
// For making conversations happen without walking around.
export const runConversation = internalAction({
args: {
maxMessages: v.optional(v.number()),
conversationCount: v.optional(v.number()),
},
handler: async (ctx, args) => {
const { players, world } = await ctx.runQuery(internal.testing.getDebugPlayers);
const memory = MemoryDB(ctx);
for (let i = 0; i < (args.conversationCount ?? 1); i++) {
await handleAgentInteraction(ctx, players, memory, async (agentId, activity) => {
console.log({ agentId, activity });
});
}
},
});
export const debugClearAll = internalAction({
args: {},
handler: async (ctx, args) => {
for (const table in schema.tables) {
await ctx.scheduler.runAfter(0, internal.crons.vacuumOldEntries, {
table: table as TableNames,
age: -1_000, // Delete 1 second into the future
cursor: null,
soFar: 0,
});
await deleteAllVectors(table as TableNames);
}
},
});

cat-town/convex/engine.ts

Lines 12 to 151 in 08b994e

// Fetch the first recent heartbeat.
if (!(await getRecentHeartbeat(ctx.db, worldId))) {
console.debug("Didn't tick: no heartbeat recently");
return;
}
const world = await ctx.db.get(worldId);
if (!world) throw new Error("Didn't tick: No world found");
if (world.frozen && !noSchedule) throw new Error("Didn't tick: world frozen");
// Fetch agents to wake up: not already thinking
const agentDocs = await ctx.db
.query('agents')
.withIndex('by_worldId_thinking', (q) => q.eq('worldId', worldId).eq('thinking', false))
.collect();
if (!agentDocs.length) {
console.debug("Didn't tick: all agents thinking");
return;
}
const agentsEagerToWake = agentDocs.filter((a) => a.nextWakeTs && a.nextWakeTs <= ts);
const agentIdsToWake = new Set([
...agentsEagerToWake.flatMap((a) => [a._id, ...(a.alsoWake ?? [])]),
]);
const nextToWake = agentDocs.find((a) => !agentIdsToWake.has(a._id) && a.nextWakeTs > ts);
if (nextToWake && !nextToWake.scheduled) {
await ctx.db.patch(nextToWake._id, { scheduled: true });
await ctx.scheduler.runAt(nextToWake.nextWakeTs, internal.engine.tick, {
worldId,
});
}
if (!agentsEagerToWake.length) {
console.debug("Didn't tick: spurious, no agents eager to wake up");
return;
}
const agentsToWake = pruneNull(await asyncMap(agentIdsToWake, ctx.db.get)).filter(
(a) => !a.thinking,
);
for (const agentDoc of agentsToWake) {
await ctx.db.patch(agentDoc._id, { thinking: true, lastWakeTs: ts });
}
const playerIds = agentsToWake.map((a) => a.playerId);
await ctx.scheduler.runAfter(0, internal.agent.runAgentBatch, { playerIds, noSchedule });
},
});
async function getRecentHeartbeat(db: DatabaseReader, worldId: Id<'worlds'>) {
return (
db
.query('heartbeats')
// Tip: by fetching heartbeats this way, the transaction doesn't conflict
// with new heartbeats being added, assuming it wasn't the first heartbeat
.withIndex('by_creation_time', (q) =>
q.gt('_creationTime', Date.now() - WORLD_IDLE_THRESHOLD),
)
.first()
);
}
export const agentDone = internalMutation({
args: {
agentId: v.id('agents'),
otherAgentIds: v.optional(v.array(v.id('agents'))),
wakeTs: v.number(),
noSchedule: v.optional(v.boolean()),
},
handler: async (ctx, args) => {
const agentDoc = await ctx.db.get(args.agentId);
if (!agentDoc) throw new Error(`Agent ${args.agentId} not found`);
if (!agentDoc.thinking) {
throw new Error('Agent was not thinking: did you call agentDone twice for the same agent?');
}
const nextWakeTs = Math.ceil(args.wakeTs / TICK_DEBOUNCE) * TICK_DEBOUNCE;
await ctx.db.replace(args.agentId, {
playerId: agentDoc.playerId,
worldId: agentDoc.worldId,
thinking: false,
lastWakeTs: agentDoc.nextWakeTs,
nextWakeTs,
alsoWake: args.otherAgentIds,
scheduled: await enqueueAgentWake(
ctx,
args.agentId,
agentDoc.worldId,
nextWakeTs,
args.noSchedule,
),
});
},
});
export async function enqueueAgentWake(
ctx: MutationCtx,
agentId: Id<'agents'>,
worldId: Id<'worlds'>,
atTs: number,
noSchedule?: boolean,
) {
// Future: Debounce wakups by looking 100ms into the future.
const nextScheduled = await ctx.db
.query('agents')
.withIndex('by_worldId_thinking', (q) =>
q.eq('worldId', worldId).eq('thinking', false).lte('nextWakeTs', atTs),
)
.first();
if (nextScheduled) {
if (!nextScheduled.scheduled) {
throw new Error("Next scheduled agent isn't scheduled: " + JSON.stringify(nextScheduled));
}
// We are effectively scheduled since it'll wake up at the same time.
if (nextScheduled.nextWakeTs === atTs) {
return true;
}
// Another agent will be scheduled before us
if (nextScheduled._id !== agentId) {
return false;
}
}
if (!noSchedule) await ctx.scheduler.runAt(atTs, internal.engine.tick, { worldId });
return true;
}
export const freezeAll = internalMutation({
args: {},
handler: async (ctx, args) => {
const worlds = await ctx.db.query('worlds').collect();
for (const world of worlds) {
await ctx.db.patch(world._id, { frozen: true });
}
},
});
export const unfreeze = internalMutation({
args: { worldId: v.optional(v.id('worlds')) },
handler: async (ctx, args) => {
const world = await ctx.db.query('worlds').order('desc').first();
if (!world) throw new Error("Didn't unfreeze: No world found");
await ctx.db.patch(world._id, { frozen: false });
await ctx.scheduler.runAfter(0, internal.engine.tick, { worldId: world._id });
},
});

handler: async (ctx, { playerId, numberOfItems }) => {
const conversations = await ctx.db
.query('memories')
.withIndex('by_playerId_type', (q) =>
//TODO - we should get memories of other types once we can
// Probably with an index just on playerId, so we can sort by time
q.eq('playerId', playerId).eq('data.type', 'conversation'),
)
.order('desc')
.take(numberOfItems);
console.debug('conversation memories lenth', conversations.length);
const reflections = await ctx.db
.query('memories')
.withIndex('by_playerId_type', (q) =>
q.eq('playerId', playerId).eq('data.type', 'reflection'),
)
.order('desc')
.take(numberOfItems);
const lastReflection = await ctx.db
.query('memories')
.withIndex('by_playerId_type', (q) =>
q.eq('playerId', playerId).eq('data.type', 'reflection'),
)
.order('desc')
.first();
const mergedList = reflections.concat(conversations);
mergedList.sort((a, b) => b._creationTime - a._creationTime);
return {
memories: mergedList.slice(0, numberOfItems),
lastReflectionTs: lastReflection?._creationTime,
};
},
});
export const getRecentMessages = internalQuery({
args: {
playerId: v.id('players'),
conversationId: v.id('conversations'),
},
handler: async (ctx, { playerId, conversationId }) => {
// Fetch the first message to bound the search for the last memory.
// Only a slight optimization for memory search, which might scan to the
// beginning of time (for this user's conversations).
const firstMessage = (await ctx.db
.query('journal')
.withIndex('by_conversation', (q) => q.eq('data.conversationId', conversationId as any))
.first()) as EntryOfType<'talking'>;
// Look for the last conversation memory for this conversation
// Only need to check from when the first message exists.
const lastConversationMemory = (await ctx.db
.query('memories')
.withIndex('by_playerId_type', (q) =>
q
.eq('playerId', playerId)
.eq('data.type', 'conversation')
.gt('_creationTime', firstMessage._creationTime),
)
.order('desc')
.filter((q) => q.eq(q.field('data.conversationId'), conversationId))
.first()) as MemoryOfType<'conversation'> | null;
const allMessages = (await ctx.db
.query('journal')
.withIndex('by_conversation', (q) => {
const q2 = q.eq('data.conversationId', conversationId as any);
if (lastConversationMemory) {
// If we have a memory of this conversation, only look at messages after.
return q2.gt('_creationTime', lastConversationMemory._creationTime);
}
return q2;
})
.filter((q) => q.eq(q.field('data.type'), 'talking'))
.collect()) as EntryOfType<'talking'>[];

cat-town/convex/chat.ts

Lines 39 to 112 in 08b994e

.order('desc')
.paginate(args.paginationOpts)) as PaginationResult<EntryOfType<'talking'>>;
const clientMessage = clientMessageMapper(ctx.db);
return {
...results,
page: await asyncMap(results.page, async (message) => ({
...(await clientMessage(message)),
conversationId: message.data.conversationId,
})),
};
},
});
export const listMessages = query({
args: { conversationId: v.id('conversations') },
handler: async (ctx, args) => {
const messages = (await conversationQuery(ctx.db, args.conversationId).take(
1000,
)) as MessageEntry[];
return asyncMap(messages, clientMessageMapper(ctx.db));
},
});
export const paginateMessages = query({
args: { conversationId: v.id('conversations'), paginationOpts: paginationOptsValidator },
handler: async (ctx, args) => {
const messages = (await conversationQuery(ctx.db, args.conversationId).paginate(
args.paginationOpts,
)) as PaginationResult<MessageEntry>;
return {
...messages,
page: await asyncMap(messages.page, clientMessageMapper(ctx.db)),
};
},
});
function conversationQuery(db: DatabaseReader, conversationId: Id<'conversations'>) {
return (
db
.query('journal')
.withIndex('by_conversation', (q) => q.eq('data.conversationId', conversationId as any))
// .filter((q) => q.eq(q.field('data.type'), 'talking'))
.order('desc')
);
}
export function clientMessageMapper(db: DatabaseReader) {
const getName = async (id: Id<'players'>) => (await db.get(id))?.name || '<Anonymous>';
const clientMessage = async (m: MessageEntry): Promise<Message> => {
const common = {
from: m.playerId,
fromName: await getName(m.playerId),
to: m.data.audience,
toNames: await asyncMap(m.data.audience, getName),
ts: m._creationTime,
};
return m.data.type === 'talking'
? {
...common,
type: 'responded',
content: m.data.content,
}
: m.data.type === 'startConversation'
? {
...common,
type: 'started',
}
: {
...common,
type: 'left',
};
};
return clientMessage;
}


Step 2: 🧐 Snippet Analysis

From looking through the relevant snippets, I decided to make the following modifications:

File Path Proposed Changes
convex/agent.ts Modify convex/agent.ts with contents:
* At the top of the file, import the 'sleep' function from the 'utils' file in the 'lib' directory. If it doesn't exist, create it. The function should use the setTimeout function to create a promise that resolves after a specified number of milliseconds. The code should look like this: "import { sleep } from './lib/utils';"
• In the 'handleAgentInteraction' function, find the part of the code where the messages are being sent in the conversation. This should be inside the while loop that continues until the conversation ends.
• After each message is sent, call the sleep function with the number of milliseconds you want to delay. The number of milliseconds can be a constant defined at the top of the file. The code should look like this: "await sleep(CONVERSATION_DELAY);"

Step 3: 📝 Planning

I have created a plan for writing the pull request. I am now working my plan and coding the required changes to address this issue. Here is the planned pull request:

Add delay function to slow down conversations in agent.ts
sweep/add-delay-function

Description

This PR adds a delay function to slow down conversations in the agent.ts file. The delay function pauses the execution of the conversation for a specified period of time after each message is sent.

Summary of Changes

  • Added import statement for the sleep function from the utils file in the lib directory
  • Introduced a delay function using setTimeout in the utils file
  • Called the delay function after each message is sent in the handleAgentInteraction function
  • Added a constant for the conversation delay at the top of the file

Step 4: ⌨️ Coding

File Instructions Progress
convex/agent.ts Modify convex/agent.ts with contents:
* At the top of the file, import the 'sleep' function from the 'utils' file in the 'lib' directory. If it doesn't exist, create it. The function should use the setTimeout function to create a promise that resolves after a specified number of milliseconds. The code should look like this: "import { sleep } from './lib/utils';"
• In the 'handleAgentInteraction' function, find the part of the code where the messages are being sent in the conversation. This should be inside the while loop that continues until the conversation ends.
• After each message is sent, call the sleep function with the number of milliseconds you want to delay. The number of milliseconds can be a constant defined at the top of the file. The code should look like this: "await sleep(CONVERSATION_DELAY);"
✅ Commit b94abdc

Step 5: 🔁 Code Review

Here are my self-reviews of my changes at sweep/add-delay-function.

Here is the 1st review

No changes required. The code changes in the pull request are well implemented. The import of the 'sleep' function, the declaration of the 'CONVERSATION_DELAY' constant, and the use of the 'sleep' function inside the 'handleAgentInteraction' function are all correctly done. Keep up the good work!

I finished incorporating these changes.


To recreate the pull request edit the issue title or description.
Join Our Discord

@sweep-ai sweep-ai bot linked a pull request Aug 16, 2023 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
sweep Assigns Sweep to an issue or pull request.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant