Skip to content

fix(kosong/kimi): move parent type into anyOf/oneOf/allOf items#2182

Open
josephkehan-prog wants to merge 1 commit into
MoonshotAI:mainfrom
josephkehan-prog:fix/kosong-kimi-combinator-parent-type
Open

fix(kosong/kimi): move parent type into anyOf/oneOf/allOf items#2182
josephkehan-prog wants to merge 1 commit into
MoonshotAI:mainfrom
josephkehan-prog:fix/kosong-kimi-combinator-parent-type

Conversation

@josephkehan-prog

Copy link
Copy Markdown

Problem

Moonshot's server-side tool schema validator rejects JSON Schemas that declare type on the same node as a combinator:

[provider.api_error] 400 tools.function.parameters is not a valid moonshot flavored json schema,
details: <At path 'root': when using anyOf, type should be defined in anyOf items instead of the parent schema>

Any tool whose parameter schema has type alongside anyOf / oneOf / allOf — whether at the root (e.g. { "type": "object", "properties": {...}, "anyOf": [...] }) or nested under properties / not / if-then-else — is sent to the API unmodified and the request fails with HTTP 400.

Root cause

normalizeKimiToolSchema in packages/kosong/src/providers/kimi-schema.ts fills in missing type fields, but:

  1. The root schema object is never normalizedensureKimiPropertyTypes only calls recurseSchema, which visits child schemas but not the root node itself.
  2. No node handles the type+combinator rule, so such schemas pass through unchanged.

Refs #792 (same bug class, different validator detail).

Fix

Add fixCombinatorParentType(node): when a node declares both type and a combinator (anyOf / oneOf / allOf), copy the parent type into each combinator item that lacks one and delete it from the parent. Since the parent type constrained every variant anyway, distributing it into the items preserves the schema's semantics. Items that already declare their own type are left untouched.

It is called on the root (in ensureKimiPropertyTypes) and on every visited node (in normalizeProperty), so all combinator positions covered by CHILD_SCHEMA_SLOTS are handled. TYPE_COMPLETION_SKIP_KEYS already includes the combinator keywords, so the existing type-inference logic does not re-add a parent type afterwards.

Verification

Ran the patched normalizer against the failing shapes:

  • {type:"object", properties:{...}, anyOf:[{required:["input"]},{required:["runInput"]}]}
    anyOf:[{required:["input"],type:"object"},{required:["runInput"],type:"object"}], parent type removed ✓
  • nested {type:"object", anyOf:[...]} under properties.<name> → fixed recursively ✓
  • items that already declare their own type → preserved; only the parent type is dropped ✓

Note: I validated against the built dist output of this package; a maintainer may want to add a unit test case to the kosong test suite covering the type+combinator rule.

Moonshot's tool schema validator rejects nodes that declare `type` on the
same level as a combinator:

  400 tools.function.parameters is not a valid moonshot flavored json
  schema, details: <At path 'root': when using anyOf, type should be
  defined in anyOf items instead of the parent schema>

normalizeKimiToolSchema had two gaps:

1. The root schema object was never normalized — ensureKimiPropertyTypes
   only recursed into child schemas.
2. No node (root or nested) handled the type+combinator rule, so tools
   whose parameters root is e.g. { type: 'object', anyOf: [...] } (or
   that nest that shape under properties / not / if-then-else) were sent
   as-is and rejected with HTTP 400.

Add fixCombinatorParentType(), which copies the parent `type` into each
combinator item that lacks one and removes it from the parent (the parent
type constrained every variant anyway, so semantics are preserved). It
runs on the root and on every visited node. Items that already declare
their own `type` are left untouched.

Refs MoonshotAI#792
@changeset-bot

changeset-bot Bot commented Jul 25, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 5803d73

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5803d73476

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

item['type'] = node['type'];
}
}
delete node['type'];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the parent type across every combinator

When a schema contains more than one combinator, the first iteration deletes node.type, so subsequent combinators receive undefined; JSON serialization then omits those item types. For example, {type:'object', anyOf:[{required:['a']}], oneOf:[{required:['b']}]} leaves the oneOf item typeless, so Moonshot can still reject the normalized schema. Capture the parent type before the loop and delete it only after all combinators have been processed.

Useful? React with 👍 / 👎.

Comment on lines +154 to +155
if (isRecord(item) && !hasOwn(item, 'type')) {
item['type'] = node['type'];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Retain the parent constraint on explicitly typed branches

When a combinator item already has a conflicting type, leaving it untouched while deleting the parent type changes the schema's accepted values. For example, {type:'object', anyOf:[{type:'string'}, {required:['a']}]} originally rejects strings because the parent requires an object, but the normalized schema accepts them through the first branch; {type:'object', allOf:[{type:'string'}]} similarly changes from unsatisfiable to a string schema. The rewrite must preserve the parent constraint for explicitly typed items as well, or reject conflicting schemas rather than broadening them.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant