Skip to content

Commit 31d2dac

Browse files
committed
Logging and updates
1 parent fdc7f7c commit 31d2dac

File tree

8 files changed

+239
-51
lines changed

8 files changed

+239
-51
lines changed

.github/workflows/deploy.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ on:
44
push:
55
branches: [main]
66
paths:
7-
- "apps/interaction-worker/**"
7+
- "apps/**"
88
- "packages/**"
99
- ".github/workflows/deploy.yml"
1010

@@ -17,9 +17,7 @@ jobs:
1717
name: Deploy to Production
1818
runs-on: ubuntu-latest
1919
timeout-minutes: 15
20-
environment:
21-
name: production
22-
url: https://poppy-interaction-production.caelinsutch.workers.dev
20+
environment: production
2321

2422
steps:
2523
- name: Checkout code

apps/execution-worker/src/durable-objects/execution-agent.ts

Lines changed: 117 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ export class ExecutionAgent extends Agent<WorkerEnv, ExecutionState> {
2323
): Promise<{ success: boolean; result?: unknown; error?: string }> {
2424
// Check if already executing to prevent concurrent runs
2525
if (this.state.isExecuting) {
26+
logger
27+
.withTags({
28+
agentId: input.agentId,
29+
conversationId: input.conversationId,
30+
})
31+
.warn("ExecutionAgent: Attempted concurrent execution", {
32+
taskDescription: input.taskDescription.substring(0, 100),
33+
});
34+
2635
return {
2736
success: false,
2837
error: "Agent is already executing a task",
@@ -37,21 +46,69 @@ export class ExecutionAgent extends Agent<WorkerEnv, ExecutionState> {
3746
conversationId: input.conversationId,
3847
})
3948
.info("ExecutionAgent: Starting task execution", {
40-
taskDescription: input.taskDescription,
49+
taskDescription: input.taskDescription.substring(0, 200),
50+
taskLength: input.taskDescription.length,
4151
});
4252

4353
try {
4454
const db = getDb(this.env.HYPERDRIVE.connectionString);
55+
56+
logger
57+
.withTags({
58+
agentId: input.agentId,
59+
conversationId: input.conversationId,
60+
})
61+
.info("ExecutionAgent: Updating agent status to active");
62+
4563
await updateAgentStatus(db, input.agentId, "active");
4664

4765
// Get agent details from database
66+
logger
67+
.withTags({
68+
agentId: input.agentId,
69+
conversationId: input.conversationId,
70+
})
71+
.info("ExecutionAgent: Fetching agent record from database");
72+
4873
const agentRecord = await db.query.agents.findFirst({
4974
where: (agents, { eq }) => eq(agents.id, input.agentId),
5075
});
5176

77+
if (!agentRecord) {
78+
logger
79+
.withTags({
80+
agentId: input.agentId,
81+
conversationId: input.conversationId,
82+
})
83+
.error("ExecutionAgent: Agent record not found");
84+
85+
throw new Error("Agent record not found");
86+
}
87+
5288
const agentName = agentRecord?.purpose || "execution_agent";
5389

90+
logger
91+
.withTags({
92+
agentId: input.agentId,
93+
conversationId: input.conversationId,
94+
})
95+
.info("ExecutionAgent: Agent record retrieved", {
96+
agentName,
97+
purpose: agentRecord.purpose,
98+
status: agentRecord.status,
99+
});
100+
54101
// Create agentic loop with tools
102+
logger
103+
.withTags({
104+
agentId: input.agentId,
105+
conversationId: input.conversationId,
106+
})
107+
.info("ExecutionAgent: Creating ToolLoopAgent", {
108+
availableTools: ["research", "wait"],
109+
maxSteps: 20,
110+
});
111+
55112
const agent = new ToolLoopAgent({
56113
model: gemini25(this.env.OPENROUTER_API_KEY),
57114
instructions: `You are the assistant of Poke by the Interaction Company of California. You are the "execution engine" of Poke, helping complete tasks for Poke, while Poke talks to the user. Your job is to execute and accomplish a goal, and you do not have direct access to the user.
@@ -89,6 +146,13 @@ ${input.taskDescription}`,
89146
stopWhen: stepCountIs(20),
90147
});
91148

149+
logger
150+
.withTags({
151+
agentId: input.agentId,
152+
conversationId: input.conversationId,
153+
})
154+
.info("ExecutionAgent: Starting agent generation");
155+
92156
const result = await agent.generate({
93157
messages: [
94158
{
@@ -98,13 +162,33 @@ ${input.taskDescription}`,
98162
],
99163
});
100164

165+
logger
166+
.withTags({
167+
agentId: input.agentId,
168+
conversationId: input.conversationId,
169+
})
170+
.info("ExecutionAgent: Agent generation completed", {
171+
stepsExecuted: result.steps?.length || 0,
172+
outputLength: result.text?.length || 0,
173+
usage: result.usage,
174+
});
175+
101176
// Extract the final result
102177
const finalResult = {
103178
output: result.text,
104179
usage: result.usage,
105180
steps: result.steps?.length || 0,
106181
};
107182

183+
logger
184+
.withTags({
185+
agentId: input.agentId,
186+
conversationId: input.conversationId,
187+
})
188+
.info("ExecutionAgent: Updating agent status to completed", {
189+
outputPreview: result.text?.substring(0, 100),
190+
});
191+
108192
await updateAgentStatus(db, input.agentId, "completed", {
109193
result: finalResult,
110194
});
@@ -116,16 +200,15 @@ ${input.taskDescription}`,
116200
})
117201
.info("ExecutionAgent: Task completed successfully", {
118202
steps: finalResult.steps,
203+
totalTokens: result.usage?.totalTokens,
119204
});
120205

121206
this.setState({ isExecuting: false });
122207
return { success: true, result: finalResult };
123208
} catch (error) {
124209
const errorMessage =
125210
error instanceof Error ? error.message : String(error);
126-
127-
const db = getDb(this.env.HYPERDRIVE.connectionString);
128-
await updateAgentStatus(db, input.agentId, "failed", { errorMessage });
211+
const errorStack = error instanceof Error ? error.stack : undefined;
129212

130213
logger
131214
.withTags({
@@ -134,7 +217,37 @@ ${input.taskDescription}`,
134217
})
135218
.error("ExecutionAgent: Task execution failed", {
136219
error: errorMessage,
220+
errorType: error instanceof Error ? error.constructor.name : typeof error,
221+
stack: errorStack,
222+
});
223+
224+
try {
225+
const db = getDb(this.env.HYPERDRIVE.connectionString);
226+
227+
logger
228+
.withTags({
229+
agentId: input.agentId,
230+
conversationId: input.conversationId,
231+
})
232+
.info("ExecutionAgent: Updating agent status to failed");
233+
234+
await updateAgentStatus(db, input.agentId, "failed", {
235+
errorMessage,
137236
});
237+
} catch (statusUpdateError) {
238+
logger
239+
.withTags({
240+
agentId: input.agentId,
241+
conversationId: input.conversationId,
242+
})
243+
.error("ExecutionAgent: Failed to update agent status", {
244+
originalError: errorMessage,
245+
statusUpdateError:
246+
statusUpdateError instanceof Error
247+
? statusUpdateError.message
248+
: String(statusUpdateError),
249+
});
250+
}
138251

139252
this.setState({ isExecuting: false });
140253
return { success: false, error: errorMessage };
Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { logger } from "@poppy/hono-helpers";
12
import { tool } from "ai";
23
import Exa from "exa-js";
34
import { z } from "zod";
@@ -13,30 +14,51 @@ export const createResearchTool = (apiKey: string) => {
1314
.describe("Maximum number of results to return (default: 5)"),
1415
}),
1516
execute: async ({ query, maxResults = 5 }) => {
16-
const exa = new Exa(apiKey);
17-
18-
const searchResults = await exa.searchAndContents(query, {
19-
numResults: maxResults,
20-
text: {
21-
maxCharacters: 500,
22-
},
23-
highlights: {
24-
numSentences: 3,
25-
},
17+
logger.info("Research tool: Starting web search", {
18+
query,
19+
maxResults,
2620
});
2721

28-
const results = searchResults.results.map((result) => ({
29-
title: result.title,
30-
url: result.url,
31-
snippet: result.text || result.highlights?.[0] || "",
32-
publishedDate: result.publishedDate,
33-
}));
22+
try {
23+
const exa = new Exa(apiKey);
3424

35-
return {
36-
type: "research_results" as const,
37-
results,
38-
query,
39-
};
25+
const searchResults = await exa.searchAndContents(query, {
26+
numResults: maxResults,
27+
text: {
28+
maxCharacters: 500,
29+
},
30+
highlights: {
31+
numSentences: 3,
32+
},
33+
});
34+
35+
const results = searchResults.results.map((result) => ({
36+
title: result.title,
37+
url: result.url,
38+
snippet: result.text || result.highlights?.[0] || "",
39+
publishedDate: result.publishedDate,
40+
}));
41+
42+
logger.info("Research tool: Search completed successfully", {
43+
query,
44+
resultCount: results.length,
45+
urls: results.map((r) => r.url),
46+
});
47+
48+
return {
49+
type: "research_results" as const,
50+
results,
51+
query,
52+
};
53+
} catch (error) {
54+
const errorMessage =
55+
error instanceof Error ? error.message : String(error);
56+
logger.error("Research tool: Search failed", {
57+
query,
58+
error: errorMessage,
59+
});
60+
throw error;
61+
}
4062
},
4163
});
4264
};

apps/execution-worker/src/tools/wait.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { logger } from "@poppy/hono-helpers";
12
import { tool } from "ai";
23
import { z } from "zod";
34

@@ -9,13 +10,29 @@ export const createWaitTool = () => {
910
reason: z.string().optional().describe("Optional reason for waiting"),
1011
}),
1112
execute: async ({ seconds, reason }) => {
13+
logger.info("Wait tool: Starting wait", {
14+
seconds,
15+
reason,
16+
});
17+
18+
const startTime = Date.now();
1219
await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
13-
return {
20+
const actualWaitTime = (Date.now() - startTime) / 1000;
21+
22+
const result = {
1423
type: "wait_complete" as const,
1524
seconds,
1625
reason,
1726
completedAt: new Date().toISOString(),
1827
};
28+
29+
logger.info("Wait tool: Wait completed", {
30+
requestedSeconds: seconds,
31+
actualSeconds: actualWaitTime.toFixed(2),
32+
reason,
33+
});
34+
35+
return result;
1936
},
2037
});
2138
};

apps/interaction-worker/env.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ type LocalEnv = import("./src/context").WorkerEnv;
33

44
// Add Env to Cloudflare namespace so that we can access it via
55
declare namespace Cloudflare {
6-
interface Env extends LocalEnv {}
6+
interface Env extends LocalEnv {
7+
}
78
}
89

910
declare module "cloudflare:test" {

apps/interaction-worker/src/services/process-message/generate-response.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ ${participants.map((p) => `- ${p.id}: ${p.phoneNumber}`).join("\n")}
116116
tools: {
117117
send_message_to_agent: createSendMessageToAgentTool(
118118
db,
119-
env,
120119
interactionAgentId,
121120
conversation.id,
122121
),

apps/interaction-worker/src/tools/send-message-to-agent.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { messages as messagesTable } from "@poppy/db";
33
import { logger } from "@poppy/hono-helpers";
44
import { generateId, tool } from "ai";
55
import { z } from "zod";
6+
import { env } from "cloudflare:workers";
67
import type { WorkerEnv } from "../context";
78
import {
89
createExecutionAgent,
@@ -13,7 +14,6 @@ type Database = ReturnType<typeof getDb>;
1314

1415
export const createSendMessageToAgentTool = (
1516
db: Database,
16-
env: WorkerEnv,
1717
interactionAgentId: string,
1818
conversationId: string,
1919
) => {
@@ -127,8 +127,8 @@ The agent has tools for a wide variety of tasks. Use this tool often.
127127

128128
try {
129129
// Get the Durable Object stub for this execution agent
130-
const id = env.EXECUTION_WORKER.idFromName(executionAgent.id);
131-
const stub = env.EXECUTION_WORKER.get(id) as any;
130+
const id = await (env as any).EXECUTION_WORKER.idFromName(executionAgent.id);
131+
const stub = await (env as any).EXECUTION_WORKER.get(id) as any;
132132

133133
// Call the executeTask method via RPC
134134
const result = (await stub.executeTask({

0 commit comments

Comments
 (0)