Skip to content

Commit e3d79eb

Browse files
committed
feat: updated Devin agent sample with improved error handling and type definitions
1 parent 6244f7c commit e3d79eb

File tree

6 files changed

+45
-29
lines changed

6 files changed

+45
-29
lines changed

nodejs/devin/sample-agent/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"main": "index.js",
55
"scripts": {
66
"build": "tsc",
7-
"start": "node --env-file=.env dist/index.js",
7+
"start": "node dist/index.js",
88
"test-tool": "agentsplayground"
99
},
1010
"engines": {
@@ -23,6 +23,7 @@
2323
},
2424
"devDependencies": {
2525
"@microsoft/m365agentsplayground": "^0.2.20",
26+
"@types/express": "^5.0.6",
2627
"typescript": "^5.9.2"
2728
}
2829
}

nodejs/devin/sample-agent/src/agent.ts

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import {
88
InferenceOperationType,
99
InferenceScope,
1010
InvokeAgentScope,
11+
InvokeAgentScopeDetails,
1112
ObservabilityManager,
13+
Request,
1214
TenantDetails,
1315
} from "@microsoft/agents-a365-observability";
1416
import { ClusterCategory } from "@microsoft/agents-a365-runtime";
@@ -93,15 +95,21 @@ export class A365Agent extends AgentApplication<ApplicationTurnState> {
9395
const baggageScope = new BaggageBuilder()
9496
.tenantId(tenantDetails.tenantId)
9597
.agentId(invokeAgentDetails.agentId)
96-
.correlationId(uuidv4())
9798
.agentName(invokeAgentDetails.agentName)
9899
.conversationId(context.activity.conversation?.id)
99100
.build();
100101

101102
await baggageScope.run(async () => {
103+
const request: Request = {
104+
conversationId: context.activity.conversation?.id,
105+
sessionId: context.activity.conversation?.id,
106+
content: context.activity.text || undefined,
107+
};
108+
const invokeScopeDetails: InvokeAgentScopeDetails = {};
102109
const invokeAgentScope = InvokeAgentScope.start(
103-
invokeAgentDetails,
104-
tenantDetails
110+
request,
111+
invokeScopeDetails,
112+
{ ...invokeAgentDetails, tenantId: tenantDetails.tenantId }
105113
);
106114

107115
await invokeAgentScope.withActiveSpanAsync(async () => {
@@ -197,50 +205,62 @@ export class A365Agent extends AgentApplication<ApplicationTurnState> {
197205

198206
startTypingLoop();
199207

208+
let inferenceScope!: ReturnType<typeof InferenceScope.start>;
200209
try {
201210
const inferenceDetails: InferenceDetails = {
202211
operationName: InferenceOperationType.CHAT,
203212
model: "claude-3-7-sonnet-20250219",
204213
providerName: "cognition-ai",
205214
inputTokens: Math.ceil(userMessage.length / 4), // Rough estimate
206-
responseId: `resp-${Date.now()}`,
207215
outputTokens: 0, // Will be updated after response
208216
finishReasons: undefined,
209217
};
210218

211-
const inferenceScope = InferenceScope.start(
219+
inferenceScope = InferenceScope.start(
220+
{ conversationId: turnContext.activity.conversation?.id },
212221
inferenceDetails,
213-
agentDetails,
214-
tenantDetails
222+
{ ...agentDetails, tenantId: agentDetails.tenantId || tenantDetails.tenantId }
215223
);
216224
inferenceScope.recordInputMessages([userMessage]);
217225

226+
const chunks: string[] = [];
227+
let streamErrorMessage: string | undefined;
218228
let totalResponseLength = 0;
219229
const responseStream = new Stream()
220-
.on("data", async (chunk) => {
221-
totalResponseLength += (chunk as string).length;
222-
invokeAgentScope.recordOutputMessages([`LLM Response: ${chunk}`]);
223-
inferenceScope.recordOutputMessages([`LLM Response: ${chunk}`]);
224-
await turnContext.sendActivity(chunk);
230+
.on("data", (chunk) => {
231+
const text = chunk as string;
232+
totalResponseLength += text.length;
233+
chunks.push(text);
234+
invokeAgentScope.recordOutputMessages([`LLM Response: ${text}`]);
235+
inferenceScope.recordOutputMessages([`LLM Response: ${text}`]);
225236
})
226-
.on("error", async (error) => {
237+
.on("error", (error) => {
238+
streamErrorMessage = String(error);
227239
invokeAgentScope.recordOutputMessages([`Streaming error: ${error}`]);
228240
inferenceScope.recordOutputMessages([`Streaming error: ${error}`]);
229-
await turnContext.sendActivity(error);
230241
})
231242
.on("close", () => {
232-
inferenceScope.recordOutputTokens(Math.ceil(totalResponseLength / 4)); // Rough estimate
243+
inferenceScope.recordOutputTokens(Math.ceil(totalResponseLength / 4));
233244
inferenceScope.recordFinishReasons(["stop"]);
234245
});
235246

236247
await devinClient.invokeAgent(userMessage, responseStream);
248+
stopTypingLoop();
249+
if (streamErrorMessage) {
250+
await turnContext.sendActivity("There was an error processing your request, please try again.");
251+
} else if (chunks.length > 0) {
252+
await turnContext.sendActivity(chunks.join("\n\n"));
253+
} else {
254+
await turnContext.sendActivity("Devin did not return a response. Please try again.");
255+
}
237256
} catch (error) {
257+
stopTypingLoop();
238258
invokeAgentScope.recordOutputMessages([`LLM error: ${error}`]);
239259
await turnContext.sendActivity(
240260
"There was an error processing your request"
241261
);
242262
} finally {
243-
stopTypingLoop();
263+
inferenceScope?.dispose();
244264
}
245265
}
246266

nodejs/devin/sample-agent/src/devin-client.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,8 @@ export class DevinClient implements Client {
146146
const messageContent = String(latestMessage?.message);
147147
responseStream.emit("data", messageContent);
148148
sentMessages.add(latestMessage.event_id);
149-
console.debug(`emit data event with content: ${messageContent}}`);
149+
console.debug(`emit data event with content: ${messageContent}`);
150+
break;
150151
}
151152
}
152153
}

nodejs/devin/sample-agent/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const server = app
3333
`\nServer listening to port ${port} for appId ${authConfig.clientId} debug ${process.env.DEBUG}`
3434
);
3535
})
36-
.on("error", async (err) => {
36+
.on("error", async (err: Error) => {
3737
console.error(err);
3838
process.exit(1);
3939
})

nodejs/devin/sample-agent/src/utils.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22
// Licensed under the MIT License.
33

44
import {
5-
ExecutionType,
6-
InvokeAgentDetails,
5+
AgentDetails,
76
TenantDetails,
87
} from "@microsoft/agents-a365-observability";
98
import { TurnContext } from "@microsoft/agents-hosting";
109

1110
// Helper functions to extract agent and tenant details from context
12-
export function getAgentDetails(context: TurnContext): InvokeAgentDetails {
11+
export function getAgentDetails(context: TurnContext): AgentDetails {
1312
// Extract agent ID from activity recipient - use agenticAppId (camelCase, not underscore)
1413
const agentId =
1514
(context.activity.recipient as any)?.agenticAppId ||
@@ -30,12 +29,6 @@ export function getAgentDetails(context: TurnContext): InvokeAgentDetails {
3029
(context.activity.recipient as any)?.name ||
3130
process.env.AGENT_NAME ||
3231
"Devin Agent Sample",
33-
conversationId: context.activity.conversation?.id,
34-
request: {
35-
content: context.activity.text || "Unknown text",
36-
executionType: ExecutionType.HumanToAgent,
37-
sessionId: context.activity.conversation?.id,
38-
},
3932
};
4033
}
4134

nodejs/devin/sample-agent/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@
1616
"rootDir": "src",
1717
"outDir": "dist",
1818
"tsBuildInfoFile": "dist/.tsbuildinfo"
19-
}
19+
},
20+
"exclude": ["publish", "dist", "node_modules"]
2021
}

0 commit comments

Comments
 (0)