@anthropic-ai/sdk
Version:
The official TypeScript library for the Anthropic API
97 lines • 5.7 kB
JavaScript
import { context, trace, SpanKind, SpanStatusCode } from '@opentelemetry/api';
import { SDK_HELPER_SYMBOL, wasCreatedByStainlessHelper } from "../../../internal/stainless-helper-header.mjs";
import { guardTracing, setErrorStatus } from "../../../internal/tracing.mjs";
import { loggerFor } from "../../../internal/utils/log.mjs";
import { ATTR_ANTHROPIC_TOOL_RESULT_CONTENT, ATTR_ANTHROPIC_TOOLSET_NAME, ATTR_GEN_AI_CONVERSATION_ID, ATTR_GEN_AI_OPERATION_NAME, ATTR_GEN_AI_OUTPUT_TYPE, ATTR_GEN_AI_PROVIDER_NAME, ATTR_GEN_AI_REQUEST_MODEL, ATTR_GEN_AI_TOOL_CALL_ARGUMENTS, ATTR_GEN_AI_TOOL_CALL_ID, ATTR_GEN_AI_TOOL_DESCRIPTION, ATTR_GEN_AI_TOOL_NAME, ATTR_GEN_AI_TOOL_TYPE, GEN_AI_OPERATION_NAME_VALUE_EXECUTE_TOOL, GEN_AI_OPERATION_NAME_VALUE_INVOKE_AGENT, GEN_AI_OUTPUT_TYPE_VALUE_JSON, GEN_AI_TOOL_TYPE_VALUE_FUNCTION, } from "./attributes.mjs";
import { setContentAttributes, toolResultContentBlocks } from "./content.mjs";
/**
* Starts the `anthropic.messages.tool_runner` span of one loop of the Messages tool runner, as a
* child of the current span. The runner sets its status and ends it.
*/
export function startToolRunnerSpan(client, params, conversationId) {
// A client whose spans are disabled computes nothing for one.
if (!client._tracer)
return undefined;
const hasJSONOutput = params.output_config?.format?.type === 'json_schema' || params.output_format?.type === 'json_schema';
return client._tracer.startSpan('anthropic.messages.tool_runner', {
kind: SpanKind.INTERNAL,
attributes: {
[ATTR_GEN_AI_OPERATION_NAME]: GEN_AI_OPERATION_NAME_VALUE_INVOKE_AGENT,
[ATTR_GEN_AI_PROVIDER_NAME]: client._genAIProviderName,
[ATTR_GEN_AI_REQUEST_MODEL]: params.model,
...(hasJSONOutput ? { [ATTR_GEN_AI_OUTPUT_TYPE]: GEN_AI_OUTPUT_TYPE_VALUE_JSON } : undefined),
...(conversationId != null ? { [ATTR_GEN_AI_CONVERSATION_ID]: conversationId } : undefined),
},
});
}
/**
* Runs `run`, one client tool run of a tool runner, with the run's span current, and returns its
* result. An error result fails the span with `tool_error`, unless the application has aborted
* `signal`: a canceled run gets no status and no result.
*/
export async function withToolUseSpan(client, tool, toolUse, options, run) {
// A client whose spans are disabled computes nothing for one.
const tracer = client._tracer;
if (!tracer)
return run();
const { signal, conversationId, sessionID, parentContext = context.active() } = options;
// Only a tool that the application or an MCP server defines has a description. It is not content.
const description = 'description' in tool ? tool.description : undefined;
const attributes = {
[ATTR_GEN_AI_OPERATION_NAME]: GEN_AI_OPERATION_NAME_VALUE_EXECUTE_TOOL,
[ATTR_GEN_AI_PROVIDER_NAME]: client._genAIProviderName,
[ATTR_GEN_AI_TOOL_NAME]: toolUse.name,
// Only a call to a member of a toolset, such as `computer`, has one.
...('toolset_name' in toolUse && typeof toolUse.toolset_name === 'string' ?
{ [ATTR_ANTHROPIC_TOOLSET_NAME]: toolUse.toolset_name }
: undefined),
...(description ? { [ATTR_GEN_AI_TOOL_DESCRIPTION]: description } : undefined),
[ATTR_GEN_AI_TOOL_CALL_ID]: toolUse.id,
[ATTR_GEN_AI_TOOL_TYPE]: GEN_AI_TOOL_TYPE_VALUE_FUNCTION,
...(conversationId != null ? { [ATTR_GEN_AI_CONVERSATION_ID]: conversationId } : undefined),
...(sessionID != null ? { 'anthropic.session.id': sessionID } : undefined),
// Only a Session event of a child thread has one.
...('session_thread_id' in toolUse && toolUse.session_thread_id != null ?
{ 'anthropic.session.thread.id': toolUse.session_thread_id }
: undefined),
};
const operation = toolUse.type === 'agent.tool_use' ? 'anthropic.tool_use'
: wasCreatedByStainlessHelper(tool) && tool[SDK_HELPER_SYMBOL] === 'mcpTool' ? 'anthropic.mcp_tool_use'
: 'anthropic.custom_tool_use';
const span = guardTracing(client, () => tracer.startSpan(`${operation} ${toolUse.name}`, { kind: SpanKind.INTERNAL, attributes }, parentContext));
if (!span)
return run();
let result;
try {
result = await context.with(trace.setSpan(parentContext, span), run);
return result;
}
finally {
// `run` makes an error result of what the tool throws, so no result is the runner's own failure.
const succeeded = result !== undefined && !result.isError;
const canceled = !succeeded && signal?.aborted === true;
const { contentMode, maxContentBytes } = client.openTelemetry.traces;
if (contentMode === 'content' && span.isRecording()) {
try {
setContentAttributes(span, {
[ATTR_GEN_AI_TOOL_CALL_ARGUMENTS]: toolUse.input,
[ATTR_ANTHROPIC_TOOL_RESULT_CONTENT]: result && !canceled ? toolResultContentBlocks(result.content) : undefined,
}, maxContentBytes);
}
catch (problem) {
// A failure to record telemetry, such as content that JSON cannot hold, must not fail the run.
loggerFor(client).debug('could not record the tool run on its span', problem);
}
}
guardTracing(client, () => {
if (succeeded) {
span.setStatus({ code: SpanStatusCode.OK });
}
else if (!canceled) {
setErrorStatus(span, result, 'tool_error');
}
span.end();
});
}
}
//# sourceMappingURL=tools.mjs.map