adk-typescript
Version:
TypeScript port of Google's Agent Development Kit (ADK)
156 lines (155 loc) • 6.39 kB
JavaScript
;
// NOTE:
//
// We expect that the underlying GenAI SDK will provide a certain
// level of tracing and logging telemetry aligned with Open Telemetry
// Semantic Conventions (such as logging prompts, responses,
// request properties, etc.) and so the information that is recorded by the
// Agent Development Kit should be focused on the higher-level
// constructs of the framework that are not observable by the SDK.
Object.defineProperty(exports, "__esModule", { value: true });
exports.tracer = void 0;
exports.traceToolCall = traceToolCall;
exports.traceToolResponse = traceToolResponse;
exports.traceCallLlm = traceCallLlm;
exports.traceSendData = traceSendData;
class Tracer {
constructor(serviceName) {
this.serviceName = serviceName;
}
startAsCurrentSpan(name) {
// In a real implementation, this would create and activate a new span
console.log(`Starting span: ${name} for service: ${this.serviceName}`);
return {
end: () => {
console.log(`Ending span: ${name}`);
}
};
}
}
// Create a tracer for the agent service
exports.tracer = new Tracer('gcp.vertex.agent');
// Placeholder for getting the current span in OpenTelemetry
function getCurrentSpan() {
// In a real implementation, this would return the current active span
return {
setAttribute: (key, value) => {
console.log(`Setting attribute ${key} = ${value}`);
}
};
}
/**
* Traces tool call.
*
* @param args The arguments to the tool call.
*/
function traceToolCall(args) {
const span = getCurrentSpan();
span.setAttribute('gen_ai.system', 'gcp.vertex.agent');
span.setAttribute('gcp.vertex.agent.tool_call_args', JSON.stringify(args));
}
/**
* Traces tool response event.
*
* This function records details about the tool response event as attributes on
* the current OpenTelemetry span.
*
* @param invocationContext The invocation context for the current agent run.
* @param eventId The ID of the event.
* @param functionResponseEvent The function response event which can be either
* merged function response for parallel function calls or individual
* function response for sequential function calls.
*/
function traceToolResponse(invocationContext, eventId, functionResponseEvent) {
const span = getCurrentSpan();
span.setAttribute('gen_ai.system', 'gcp.vertex.agent');
span.setAttribute('gcp.vertex.agent.invocation_id', invocationContext.invocationId);
span.setAttribute('gcp.vertex.agent.event_id', eventId);
span.setAttribute('gcp.vertex.agent.tool_response', JSON.stringify(functionResponseEvent));
// Setting empty llm request and response (as UI expect these) while not
// applicable for tool_response.
span.setAttribute('gcp.vertex.agent.llm_request', '{}');
span.setAttribute('gcp.vertex.agent.llm_response', '{}');
}
/**
* Traces a call to the LLM.
*
* This function records details about the LLM request and response as
* attributes on the current OpenTelemetry span.
*
* @param invocationContext The invocation context for the current agent run.
* @param eventId The ID of the event.
* @param llmRequest The LLM request object.
* @param llmResponse The LLM response object.
*/
function traceCallLlm(invocationContext, eventId, llmRequest, llmResponse) {
const span = getCurrentSpan();
// Special standard Open Telemetry GenaI attributes that indicate
// that this is a span related to a Generative AI system.
span.setAttribute('gen_ai.system', 'gcp.vertex.agent');
span.setAttribute('gen_ai.request.model', llmRequest.model || 'unknown');
span.setAttribute('gcp.vertex.agent.invocation_id', invocationContext.invocationId);
span.setAttribute('gcp.vertex.agent.event_id', eventId);
// Consider removing once GenAI SDK provides a way to record this info.
span.setAttribute('gcp.vertex.agent.llm_request', JSON.stringify(buildLlmRequestForTrace(llmRequest)));
// Consider removing once GenAI SDK provides a way to record this info.
span.setAttribute('gcp.vertex.agent.llm_response', JSON.stringify(llmResponse));
}
/**
* Traces the sending of data to the agent.
*
* This function records details about the data sent to the agent as
* attributes on the current OpenTelemetry span.
*
* @param invocationContext The invocation context for the current agent run.
* @param eventId The ID of the event.
* @param data A list of content objects.
*/
function traceSendData(invocationContext, eventId, data) {
const span = getCurrentSpan();
span.setAttribute('gcp.vertex.agent.invocation_id', invocationContext.invocationId);
span.setAttribute('gcp.vertex.agent.event_id', eventId);
// Once instrumentation is added to the GenAI SDK, consider whether this
// information still needs to be recorded by the Agent Development Kit.
span.setAttribute('gcp.vertex.agent.data', JSON.stringify(data.map(content => ({
role: content.role,
parts: content.parts
}))));
}
/**
* Builds a dictionary representation of the LLM request for tracing.
*
* This function prepares a dictionary representation of the LlmRequest
* object, suitable for inclusion in a trace. It excludes fields that cannot
* be serialized (e.g., function pointers) and avoids sending bytes data.
*
* @param llmRequest The LlmRequest object.
* @returns A dictionary representation of the LLM request.
* @private
*/
function buildLlmRequestForTrace(llmRequest) {
// Some fields in LlmRequest are function pointers and can not be serialized.
const result = {
model: llmRequest.model,
config: { ...llmRequest.config },
contents: []
};
// Delete response schema to avoid circular references
if (result.config.responseSchema) {
delete result.config.responseSchema;
}
// We do not want to send bytes data to the trace.
if (llmRequest.contents) {
for (const content of llmRequest.contents) {
if (!content || !content.parts)
continue;
// Filter out parts with inline_data
const filteredParts = content.parts.filter(part => !part.inlineData);
result.contents.push({
role: content.role,
parts: filteredParts
});
}
}
return result;
}