fix(tools): correct Python SDK fidelity and language-dispatch contract

Address ds-review-bot v5/v6 review on the Python SDK renderer:
- resolveFlavor now takes a peekRuntime() reader: undefined (no runtime,
  the doc-catalog harvest) degrades to the TS flavor, but a mounted
  runtime whose language is absent from RUN_CODE_FLAVORS fails loud. This
  removes the try/catch that silently swallowed the invalid-language path
  and drops the /* v8 ignore */ that hid the flavor guard from coverage;
  wireSchemas validates the runtime before projecting schemas so the
  renderer-table rejection stays the canonical assembly error.
- py-types RESERVED drops the soft keywords match/case: they are legal as
  TypedDict fields and methods, so keeping them needlessly degraded
  common search/regex arg objects to dict[str, Any].
- py-types treats an object with omitted properties as {} like the unified
  validator and TS renderer do, so a closed empty object declares an empty
  TypedDict instead of a permissive dict[str, Any].
- README: symmetric jsonSchemaToPy->Any note; a stale zh SDK bullet and
  limitation corrected; link the service-wide-language limitation to its
  Agent Note.
This commit is contained in:
Chinesezjc
2026-07-31 18:55:07 +08:00
parent 85a831259c
commit 683c92cb2e
8 changed files with 130 additions and 57 deletions
+21 -7
View File
@@ -373,13 +373,27 @@ describe('mode-aware wire contribution', () => {
expect(codeParam.description).toBe('The program: the body of an async Python function.')
})
it('fails loud when the runtime language has no run_code schema flavor', async () => {
// A language with an SDK renderer registered but (hypothetically) no schema
// flavor would fail here; a language with neither fails earlier at
// requireCodeRuntime. Both guards keep the two tables coupled.
const { ctx, systemPrompt } = await setup({ mode: 'code', runtime: { language: 'ruby' } })
registerEcho(ctx)
await expect(systemPrompt.assemble()).rejects.toThrow(/no SDK renderer registered for runtime language "ruby"/)
it('resolves the run_code schema flavor lazily and fails loud on a language absent from the flavor table', async () => {
// The flavor getter reads the runtime directly (peekRuntime), so it — not
// requireCodeRuntime — owns the flavor-table guard. A language with no
// flavor entry throws when the schema is projected, keeping
// RUN_CODE_FLAVORS coupled to SDK_RENDERERS. Assembly's requireCodeRuntime
// rejects such a language earlier; this reaches the guard on its own.
const { ctx } = await setup({ mode: 'code', runtime: { language: 'ruby' } })
const definition = ctx.tools.get(RUN_CODE_NAME)
expect(() => definition?.description).toThrow(/no run_code schema flavor registered for runtime language "ruby"/)
})
it('degrades the run_code flavor to TypeScript when no runtime is mounted (doc-catalog schema harvest)', async () => {
// The tool-catalog generator boots the registry under `mode: code` and
// reads run_code's schema WITHOUT a runtime; peekRuntime returns undefined
// there, so the flavor getter degrades to the TS default rather than
// throwing (that harvest never feeds a model).
const { ctx } = await setup({ mode: 'code', runtime: false })
const definition = ctx.tools.get(RUN_CODE_NAME)
expect(definition?.description).toContain('Execute a TypeScript program')
const params = definition?.parameters as { properties: { code: { description: string } } }
expect(params.properties.code.description).toBe('The program: the body of an async TypeScript function.')
})
it("rejects the assembly when toolOrder names a native tool that mode 'code' no longer contributes", async () => {