MCPcopy Create free account
hub / github.com/Noumena-Network/code / normalizeContentFromAPI

Function normalizeContentFromAPI

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

Source from the content-addressed store, hash-verified

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

Callers 1

queryModelFunction · 0.85

Calls 8

isObjectFunction · 0.85
isInternalBuildFunction · 0.85
findToolByNameFunction · 0.85
logForDebuggingFunction · 0.70
normalizeToolInputFunction · 0.70
logErrorFunction · 0.70
logEventFunction · 0.50

Tested by

no test coverage detected