MCPcopy Create free account
hub / github.com/claude-code-best/claude-code / normalizeContentFromAPI

Function normalizeContentFromAPI

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

Source from the content-addressed store, hash-verified

2981// Sometimes the API returns empty messages (eg. "\n\n"). We need to filter these out,
2982// otherwise they will give an API error when we send them to the API next time we call query().
2983export function normalizeContentFromAPI(
2984 contentBlocks: BetaMessage['content'],
2985 tools: Tools,
2986 agentId?: AgentId,
2987): BetaMessage['content'] {
2988 if (!contentBlocks) {
2989 return []
2990 }
2991 return contentBlocks.map(contentBlock => {
2992 switch (contentBlock.type) {
2993 case 'tool_use': {
2994 if (
2995 typeof contentBlock.input !== 'string' &&
2996 !isObject(contentBlock.input)
2997 ) {
2998 // we stream tool use inputs as strings, but when we fall back, they're objects
2999 throw new Error('Tool use input must be a string or object')
3000 }
3001
3002 // With fine-grained streaming on, we are getting a stringied JSON back from the API.
3003 // The API has strange behaviour, where it returns nested stringified JSONs, and so
3004 // we need to recursively parse these. If the top-level value returned from the API is
3005 // an empty string, this should become an empty object (nested values should be empty string).
3006 // TODO: This needs patching as recursive fields can still be stringified
3007 let normalizedInput: unknown
3008 if (typeof contentBlock.input === 'string') {
3009 const parsed = safeParseJSON(contentBlock.input)
3010 if (parsed === null && contentBlock.input.length > 0) {
3011 // TET/FC-v3 diagnostic: the streamed tool input JSON failed to
3012 // parse. We fall back to {} which means downstream validation
3013 // sees empty input. The raw prefix goes to debug log only — no
3014 // PII-tagged proto column exists for it yet.
3015 logEvent('tengu_tool_input_json_parse_fail', {
3016 toolName: sanitizeToolNameForAnalytics(contentBlock.name),
3017 inputLen: contentBlock.input.length,
3018 })
3019 if (process.env.USER_TYPE === 'ant') {
3020 logForDebugging(
3021 `tool input JSON parse fail: ${contentBlock.input.slice(0, 200)}`,
3022 { level: 'warn' },
3023 )
3024 }
3025 }
3026 normalizedInput = parsed ?? {}
3027 } else {
3028 normalizedInput = contentBlock.input
3029 }
3030
3031 // Then apply tool-specific corrections
3032 if (typeof normalizedInput === 'object' && normalizedInput !== null) {
3033 const tool = findToolByName(tools, contentBlock.name)
3034 if (tool) {
3035 try {
3036 normalizedInput = normalizeToolInput(
3037 tool,
3038 normalizedInput as { [key: string]: unknown },
3039 agentId,
3040 )

Callers 4

queryModelFunction · 0.85
queryModelGeminiFunction · 0.85
queryModelGrokFunction · 0.85

Calls 7

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

Tested by

no test coverage detected