MCPcopy Index your code
hub / github.com/codeaashu/claude-code / normalizeContentFromAPI

Function normalizeContentFromAPI

src/utils/messages.ts:2651–2751  ·  view source on GitHub ↗
(
  contentBlocks: BetaMessage['content'],
  tools: Tools,
  agentId?: AgentId,
)

Source from the content-addressed store, hash-verified

2649// Sometimes the API returns empty messages (eg. "\n\n"). We need to filter these out,
2650// otherwise they will give an API error when we send them to the API next time we call query().
2651export function normalizeContentFromAPI(
2652 contentBlocks: BetaMessage['content'],
2653 tools: Tools,
2654 agentId?: AgentId,
2655): BetaMessage['content'] {
2656 if (!contentBlocks) {
2657 return []
2658 }
2659 return contentBlocks.map(contentBlock => {
2660 switch (contentBlock.type) {
2661 case 'tool_use': {
2662 if (
2663 typeof contentBlock.input !== 'string' &&
2664 !isObject(contentBlock.input)
2665 ) {
2666 // we stream tool use inputs as strings, but when we fall back, they're objects
2667 throw new Error('Tool use input must be a string or object')
2668 }
2669
2670 // With fine-grained streaming on, we are getting a stringied JSON back from the API.
2671 // The API has strange behaviour, where it returns nested stringified JSONs, and so
2672 // we need to recursively parse these. If the top-level value returned from the API is
2673 // an empty string, this should become an empty object (nested values should be empty string).
2674 // TODO: This needs patching as recursive fields can still be stringified
2675 let normalizedInput: unknown
2676 if (typeof contentBlock.input === 'string') {
2677 const parsed = safeParseJSON(contentBlock.input)
2678 if (parsed === null && contentBlock.input.length > 0) {
2679 // TET/FC-v3 diagnostic: the streamed tool input JSON failed to
2680 // parse. We fall back to {} which means downstream validation
2681 // sees empty input. The raw prefix goes to debug log only — no
2682 // PII-tagged proto column exists for it yet.
2683 logEvent('tengu_tool_input_json_parse_fail', {
2684 toolName: sanitizeToolNameForAnalytics(contentBlock.name),
2685 inputLen: contentBlock.input.length,
2686 })
2687 if (process.env.USER_TYPE === 'ant') {
2688 logForDebugging(
2689 `tool input JSON parse fail: ${contentBlock.input.slice(0, 200)}`,
2690 { level: 'warn' },
2691 )
2692 }
2693 }
2694 normalizedInput = parsed ?? {}
2695 } else {
2696 normalizedInput = contentBlock.input
2697 }
2698
2699 // Then apply tool-specific corrections
2700 if (typeof normalizedInput === 'object' && normalizedInput !== null) {
2701 const tool = findToolByName(tools, contentBlock.name)
2702 if (tool) {
2703 try {
2704 normalizedInput = normalizeToolInput(
2705 tool,
2706 normalizedInput as { [key: string]: unknown },
2707 agentId,
2708 )

Callers 1

queryModelFunction · 0.85

Calls 6

logEventFunction · 0.85
logForDebuggingFunction · 0.85
findToolByNameFunction · 0.85
normalizeToolInputFunction · 0.85
logErrorFunction · 0.70

Tested by

no test coverage detected