perf(tools): keep py-types oneOf rendering linear in schema depth

A deep oneOf chain joined the accumulated union string at every level
(Array.join forces materialization), making it Theta(depth^2) — a
50,000-level chain took ~7.6s. Concatenate with `+` instead: V8 builds a
lazy ConsString that materializes once at the root, matching the array
arm's template-literal laziness and ts-types' composable-document approach.
The whole walk is now linear in depth. Adds a 20,000-level oneOf test
alongside the existing deep-array one; py-types.ts stays at 100% coverage.
This commit is contained in:
Chinesezjc
2026-08-02 17:22:38 +08:00
parent 282b0d7443
commit b0e405a679
2 changed files with 25 additions and 1 deletions

View File

@@ -469,6 +469,20 @@ describe('renderToolsSdkPy', () => {
expect(type.length).toBe('list['.length * 20000 + 'str'.length + ']'.repeat(20000).length)
})
it('renders a deeply nested oneOf chain in linear time (no per-level re-materialization)', () => {
// Each level is a two-branch oneOf whose first branch recurses; joining the
// accumulated union string at every level would be Theta(depth^2). The `+`
// (ConsString) concatenation keeps it linear, like the array arm.
const depth = 20000
let deep: Record<string, unknown> = { type: 'string' }
for (let i = 0; i < depth; i++) deep = { oneOf: [deep, { type: 'null' }] }
const type = jsonSchemaToPy(deep)
// depth levels of ` | None` appended to the innermost `str`.
expect(type.startsWith('str | None')).toBe(true)
expect(type.endsWith(' | None')).toBe(true)
expect(type.length).toBe('str'.length + ' | None'.length * depth)
})
it('emits pass for a subscript-only tool set (comments are not statements)', () => {
const t: ToolSdkSchema = {
name: 'my-exotic.tool',