Jelajahi Sumber

fix(tools): treat a whitespace-only description as absent in the Python SDK

It collapsed to '' rather than undefined, so the renderer emitted an empty
`""""""` docstring or a bare `#   ` line for a node that documents nothing.
Chinesezjc 2 bulan lalu
induk
melakukan
a1d7b9a3cd

+ 7 - 3
packages/core/tools/src/py-types.ts

@@ -82,7 +82,10 @@ const UNPRINTABLE = /[\u0000-\u0008\u000e-\u001f\u007f]/g
  * The collapsed one-line `description` of a schema node (byte-stable across
  * The collapsed one-line `description` of a schema node (byte-stable across
  * formatting churn), or `undefined` when the node carries none. Every caller
  * formatting churn), or `undefined` when the node carries none. Every caller
  * passes an object (validated property nodes, or the ToolSdkSchema itself),
  * passes an object (validated property nodes, or the ToolSdkSchema itself),
- * so only the description field needs guarding.
+ * so only the description field needs guarding. A description that collapses
+ * to nothing (empty, or whitespace only) is `undefined` too: it documents the
+ * node no better than an absent one, and emitting it would leave an empty
+ * `"""` docstring or a bare `#   ` line in the SDK.
  *
  *
  * Control characters left over after the whitespace collapse are rendered as
  * Control characters left over after the whitespace collapse are rendered as
  * their `\xNN` escapes (see {@link UNPRINTABLE}); the escape's own backslash is
  * their `\xNN` escapes (see {@link UNPRINTABLE}); the escape's own backslash is
@@ -91,11 +94,12 @@ const UNPRINTABLE = /[\u0000-\u0008\u000e-\u001f\u007f]/g
  */
  */
 function describe(schema: object): string | undefined {
 function describe(schema: object): string | undefined {
   const description = (schema as Record<string, unknown>).description
   const description = (schema as Record<string, unknown>).description
-  if (typeof description !== 'string' || description.length === 0) return undefined
-  return description
+  if (typeof description !== 'string') return undefined
+  const collapsed = description
     .replace(/\s+/g, ' ')
     .replace(/\s+/g, ' ')
     .replace(UNPRINTABLE, char => `\\x${char.charCodeAt(0).toString(16).padStart(2, '0')}`)
     .replace(UNPRINTABLE, char => `\\x${char.charCodeAt(0).toString(16).padStart(2, '0')}`)
     .trim()
     .trim()
+  return collapsed.length === 0 ? undefined : collapsed
 }
 }
 
 
 /**
 /**

+ 7 - 0
packages/core/tools/tests/py-types.spec.ts

@@ -464,6 +464,13 @@ describe('renderToolsSdkPy', () => {
     // Subscript entry appears without the "#   ..." description follow-up.
     // Subscript entry appears without the "#   ..." description follow-up.
     expect(text).toContain('# tools["weird-name"]')
     expect(text).toContain('# tools["weird-name"]')
     expect(text.split('\n').every(line => !line.startsWith('    #   '))).toBe(true)
     expect(text.split('\n').every(line => !line.startsWith('    #   '))).toBe(true)
+    // A whitespace-only description collapses to nothing and is treated as
+    // absent: no empty `""""""` docstring, no bare `#   ` line.
+    const blank = renderToolsSdkPy([
+      { ...undescribedIdentifier, description: ' \t\n ' },
+      { ...undescribedExotic, description: '   ' },
+    ])
+    expect(blank).toBe(text)
   })
   })
 
 
   it('marks an open object TypedDict and declares a closed empty object', () => {
   it('marks an open object TypedDict and declares a closed empty object', () => {