mirror of
https://github.com/moeru-ai/airi.git
synced 2026-09-01 14:56:52 +08:00
fix(plugin-sdk-tamagotchi): allow nullable enum tool parameters (#2314)
This commit is contained in:
@@ -10,7 +10,7 @@ import type { HostDataRecord } from '@proj-airi/plugin-sdk/plugin-host'
|
||||
import type { ToolKitRuntime } from './tools'
|
||||
|
||||
import { DisposableStore } from '@proj-airi/plugin-sdk'
|
||||
import { object, optional, string } from 'valibot'
|
||||
import { object, optional, picklist, string } from 'valibot'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import {
|
||||
@@ -731,6 +731,38 @@ describe('plugin-sdk-tamagotchi', () => {
|
||||
expect(parameters.properties.opening.type).toEqual(['string', 'null'])
|
||||
})
|
||||
|
||||
// ROOT CAUSE:
|
||||
//
|
||||
// The normalizer added null to an optional enum but kept type: "string".
|
||||
// OpenAI rejected the schema because null does not satisfy the string type.
|
||||
// The fix must allow null in both type and enum.
|
||||
it('serializes optional enum tool fields as nullable enum properties', async () => {
|
||||
const registerTool = vi.fn()
|
||||
const tools = toolKit.createClient(createToolRuntime({
|
||||
extensionId: 'airi-extension-chess',
|
||||
sessionId: 'session-1',
|
||||
moduleId: 'chess',
|
||||
register: registerTool,
|
||||
registerToolsetPrompt: vi.fn(),
|
||||
}))
|
||||
|
||||
await tools.registerTool({
|
||||
id: 'play_chess',
|
||||
title: 'placeholder-title',
|
||||
description: 'placeholder-description',
|
||||
inputSchema: object({
|
||||
airiSide: optional(picklist(['white', 'black'])),
|
||||
}),
|
||||
execute: async () => ({ ok: true }),
|
||||
})
|
||||
|
||||
const parameters = registerTool.mock.calls[0]?.[0].tool.parameters
|
||||
|
||||
expect(parameters.required).toEqual(['airiSide'])
|
||||
expect(parameters.properties.airiSide.type).toEqual(['string', 'null'])
|
||||
expect(parameters.properties.airiSide.enum).toEqual(['white', 'black', null])
|
||||
})
|
||||
|
||||
/**
|
||||
* @example
|
||||
* expect(registerBinding).toHaveBeenCalledWith(expect.objectContaining({ moduleId: 'chess:board' }))
|
||||
|
||||
@@ -171,7 +171,6 @@ function withNullableValue(schema: JsonSchema): JsonSchema {
|
||||
|
||||
if (Array.isArray(next.enum)) {
|
||||
next.enum = next.enum.includes(null) ? next.enum : [...next.enum, null]
|
||||
return next
|
||||
}
|
||||
|
||||
if (Array.isArray(next.type)) {
|
||||
@@ -184,6 +183,11 @@ function withNullableValue(schema: JsonSchema): JsonSchema {
|
||||
return next
|
||||
}
|
||||
|
||||
// An enum without a type already accepts every enum value, including null.
|
||||
if (Array.isArray(next.enum)) {
|
||||
return next
|
||||
}
|
||||
|
||||
next.anyOf = [...(next.anyOf ?? []), { type: 'null' }]
|
||||
return next
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user