Skip to content

Commit 2db6fcd

Browse files
committed
Integrate OpenTelemetry (OTel) support: 2. use telemetry functions in the code
1 parent dc49ca8 commit 2db6fcd

File tree

4 files changed

+205
-136
lines changed

4 files changed

+205
-136
lines changed

core/src/agents/base_agent.ts

Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
*/
66

77
import {Content} from '@google/genai';
8-
import {trace} from '@opentelemetry/api';
8+
import {context, trace} from '@opentelemetry/api';
99

1010
import {createEvent, Event} from '../events/event.js';
1111

1212
import {CallbackContext} from './callback_context.js';
1313
import {InvocationContext} from './invocation_context.js';
14+
import {bindAsyncGenerator, traceAgentInvocation, tracer} from '../telemetry/tracing.js';
1415

1516
type SingleAgentCallback = (context: CallbackContext) =>
1617
Promise<Content|undefined>|(Content|undefined);
@@ -124,34 +125,37 @@ export abstract class BaseAgent {
124125
async *
125126
runAsync(parentContext: InvocationContext):
126127
AsyncGenerator<Event, void, void> {
127-
const span = trace.getTracer('gcp.vertex.agent')
128-
.startSpan(`agent_run [${this.name}]`);
128+
const span = tracer.startSpan(`invoke_agent ${this.name}`);
129+
const ctx = trace.setSpan(context.active(), span);
129130
try {
130-
const context = this.createInvocationContext(parentContext);
131-
132-
const beforeAgentCallbackEvent =
133-
await this.handleBeforeAgentCallback(context);
134-
if (beforeAgentCallbackEvent) {
135-
yield beforeAgentCallbackEvent;
136-
}
137-
138-
if (context.endInvocation) {
139-
return;
140-
}
141-
142-
for await (const event of this.runAsyncImpl(context)) {
143-
yield event;
144-
}
145-
146-
if (context.endInvocation) {
147-
return;
148-
}
149-
150-
const afterAgentCallbackEvent =
151-
await this.handleAfterAgentCallback(context);
152-
if (afterAgentCallbackEvent) {
153-
yield afterAgentCallbackEvent;
154-
}
131+
yield* bindAsyncGenerator(ctx, (async function* (this: BaseAgent) {
132+
const context = this.createInvocationContext(parentContext);
133+
134+
const beforeAgentCallbackEvent =
135+
await this.handleBeforeAgentCallback(context);
136+
if (beforeAgentCallbackEvent) {
137+
yield beforeAgentCallbackEvent;
138+
}
139+
140+
if (context.endInvocation) {
141+
return;
142+
}
143+
144+
traceAgentInvocation({agent: this, invocationContext: context});
145+
for await (const event of this.runAsyncImpl(context)) {
146+
yield event;
147+
}
148+
149+
if (context.endInvocation) {
150+
return;
151+
}
152+
153+
const afterAgentCallbackEvent =
154+
await this.handleAfterAgentCallback(context);
155+
if (afterAgentCallbackEvent) {
156+
yield afterAgentCallbackEvent;
157+
}
158+
}).call(this));
155159
} finally {
156160
span.end();
157161
}
@@ -167,10 +171,12 @@ export abstract class BaseAgent {
167171
async *
168172
runLive(parentContext: InvocationContext):
169173
AsyncGenerator<Event, void, void> {
170-
const span = trace.getTracer('gcp.vertex.agent')
171-
.startSpan(`agent_run [${this.name}]`);
174+
const span = tracer.startSpan(`invoke_agent ${this.name}`);
175+
const ctx = trace.setSpan(context.active(), span);
172176
try {
173-
// TODO(b/425992518): Implement live mode.
177+
yield* bindAsyncGenerator(ctx, (async function* (this: BaseAgent) {
178+
// TODO(b/425992518): Implement live mode.
179+
}).call(this));
174180
throw new Error('Live mode is not implemented yet.');
175181
} finally {
176182
span.end();

core/src/agents/functions.ts

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {randomUUID} from '../utils/env_aware_utils.js';
1717
import {logger} from '../utils/logger.js';
1818

1919
import {SingleAfterToolCallback, SingleBeforeToolCallback} from './llm_agent.js';
20+
import {traceMergedToolCalls, tracer, traceToolCall} from '../telemetry/tracing.js';
2021

2122
const AF_FUNCTION_CALL_ID_PREFIX = 'adk-';
2223
export const REQUEST_EUC_FUNCTION_CALL_NAME = 'adk_request_credential';
@@ -191,11 +192,53 @@ async function callToolAsync(
191192
args: Record<string, any>,
192193
toolContext: ToolContext,
193194
): Promise<any> {
194-
// TODO - b/436079721: implement [tracer.start_as_current_span]
195-
logger.debug(`callToolAsync ${tool.name}`);
196-
return await tool.runAsync({args, toolContext});
195+
const span = tracer.startSpan(`execute_tool ${tool.name}`);
196+
try {
197+
logger.debug(`callToolAsync ${tool.name}`);
198+
const result = await tool.runAsync({args, toolContext});
199+
traceToolCall({
200+
tool,
201+
args,
202+
functionResponseEvent: buildResponseEvent(tool, result, toolContext, toolContext.invocationContext)
203+
})
204+
return result;
205+
} finally {
206+
span.end();
207+
}
197208
}
198209

210+
function buildResponseEvent(
211+
tool: BaseTool,
212+
functionResult: any,
213+
toolContext: ToolContext,
214+
invocationContext: InvocationContext,
215+
): Event {
216+
let responseResult = functionResult;
217+
if (typeof functionResult !== 'object' || functionResult == null) {
218+
responseResult = {result: functionResult};
219+
}
220+
221+
const partFunctionResponse: Part = {
222+
functionResponse: {
223+
name: tool.name,
224+
response: responseResult,
225+
id: toolContext.functionCallId,
226+
},
227+
};
228+
229+
const content: Content = {
230+
role: 'user',
231+
parts: [partFunctionResponse],
232+
};
233+
234+
return createEvent({
235+
invocationId: invocationContext.invocationId,
236+
author: invocationContext.agent.name,
237+
content: content,
238+
actions: toolContext.actions,
239+
branch: invocationContext.branch,
240+
});
241+
}
199242
/**
200243
* Handles function calls.
201244
* Runtime behavior to pay attention to:
@@ -426,12 +469,18 @@ export async function handleFunctionCallList({
426469

427470
if (functionResponseEvents.length > 1) {
428471
// TODO - b/436079721: implement [tracer.start_as_current_span]
429-
logger.debug('execute_tool (merged)');
430-
// TODO - b/436079721: implement [traceMergedToolCalls]
431-
logger.debug('traceMergedToolCalls', {
432-
responseEventId: mergedEvent.id,
433-
functionResponseEvent: mergedEvent.id,
434-
});
472+
const span = tracer.startSpan('execute_tool (merged)');
473+
try {
474+
logger.debug('execute_tool (merged)');
475+
// TODO - b/436079721: implement [traceMergedToolCalls]
476+
logger.debug('traceMergedToolCalls', {
477+
responseEventId: mergedEvent.id,
478+
functionResponseEvent: mergedEvent.id,
479+
});
480+
traceMergedToolCalls({responseEventId: mergedEvent.id, functionResponseEvent: mergedEvent});
481+
} finally {
482+
span.end();
483+
}
435484
}
436485
return mergedEvent;
437486
}

core/src/agents/llm_agent.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66

77
import {FunctionCall, GenerateContentConfig, Schema} from '@google/genai';
8+
import {context, trace} from '@opentelemetry/api';
89
import {z} from 'zod';
910

1011
import {createEvent, createNewEventId, Event, getFunctionCalls, getFunctionResponses, isFinalResponse} from '../events/event.js';
@@ -30,6 +31,7 @@ import {injectSessionState} from './instructions.js';
3031
import {InvocationContext} from './invocation_context.js';
3132
import {ReadonlyContext} from './readonly_context.js';
3233
import {StreamingMode} from './run_config.js';
34+
import {bindAsyncGenerator, traceCallLlm, tracer} from '../telemetry/tracing.js';
3335

3436
/** An object that can provide an instruction string. */
3537
export type InstructionProvider = (
@@ -1055,7 +1057,10 @@ export class LlmAgent extends BaseAgent {
10551057
author: this.name,
10561058
branch: invocationContext.branch,
10571059
});
1058-
for await (const llmResponse of this.callLlmAsync(
1060+
const span = tracer.startSpan('call_llm');
1061+
const ctx = trace.setSpan(context.active(), span);
1062+
yield* bindAsyncGenerator(ctx, (async function* (this: LlmAgent) {
1063+
for await (const llmResponse of this.callLlmAsync(
10591064
invocationContext, llmRequest, modelResponseEvent)) {
10601065
// ======================================================================
10611066
// Postprocess after calling the LLM
@@ -1066,8 +1071,10 @@ export class LlmAgent extends BaseAgent {
10661071
modelResponseEvent.id = createNewEventId();
10671072
modelResponseEvent.timestamp = new Date().getTime();
10681073
yield event;
1074+
}
10691075
}
1070-
}
1076+
}).call(this));
1077+
span.end();
10711078
}
10721079

10731080
private async *
@@ -1217,7 +1224,6 @@ export class LlmAgent extends BaseAgent {
12171224

12181225
// Calls the LLM.
12191226
const llm = this.canonicalModel;
1220-
// TODO - b/436079721: Add tracer.start_as_current_span('call_llm')
12211227
if (invocationContext.runConfig?.supportCfc) {
12221228
// TODO - b/425992518: Implement CFC call path
12231229
// This is a hack, underneath it calls runLive. Which makes
@@ -1234,8 +1240,12 @@ export class LlmAgent extends BaseAgent {
12341240
for await (const llmResponse of this.runAndHandleError(
12351241
responsesGenerator, invocationContext, llmRequest,
12361242
modelResponseEvent)) {
1237-
// TODO - b/436079721: Add trace_call_llm
1238-
1243+
traceCallLlm({
1244+
invocationContext,
1245+
eventId: modelResponseEvent.id,
1246+
llmRequest,
1247+
llmResponse,
1248+
});
12391249
// Runs after_model_callback if it exists.
12401250
const alteredLlmResponse = await this.handleAfterModelCallback(
12411251
invocationContext, llmResponse, modelResponseEvent);

0 commit comments

Comments
 (0)