diff --git a/src/ax/dsp/input-object-array.test.ts b/src/ax/dsp/input-object-array.test.ts new file mode 100644 index 00000000..e6ca503e --- /dev/null +++ b/src/ax/dsp/input-object-array.test.ts @@ -0,0 +1,74 @@ +import { vi, describe, it, expect } from 'vitest'; + +import { ax } from '../index.js'; +import { f } from './sig.js'; + +const sig = f() + .input( + 'items', + f + .object( + { + id: f.number('Index'), + name: f.string('Name'), + }, + 'Item' + ) + .array() + ) + .output( + 'classifiedItems', + f + .object( + { + id: f.number('Index'), + label: f.string('Label'), + }, + 'Classification' + ) + .array() + ) + .build(); + +const gen = ax(sig); + +import type { AxAIService, AxChatResponse } from '../ai/types.js'; + +const llm: AxAIService = { + chat: vi.fn().mockResolvedValue({ + results: [ + { + index: 0, + content: + 'Classified Items: [{"id": 0, "label": "A"}, {"id": 1, "label": "B"}]', + }, + ], + } as AxChatResponse), + embed: vi.fn(), + getOptions: vi.fn().mockReturnValue({}), + setOptions: vi.fn(), + getName: vi.fn(), + getFeatures: vi.fn(), + getModelList: vi.fn(), + getMetrics: vi.fn(), + getLogger: vi.fn(), + getLastUsedChatModel: vi.fn(), + getLastUsedEmbedModel: vi.fn(), + getLastUsedModelConfig: vi.fn(), + getId: vi.fn(), +}; + +describe('f.object().array() on inputs', () => { + it('should not throw a validation error for valid input', async () => { + const result = await gen.forward(llm, { + items: [ + { id: 0, name: 'Foo' }, + { id: 1, name: 'Bar' }, + ], + }); + expect(result.classifiedItems).toEqual([ + { id: 0, label: 'A' }, + { id: 1, label: 'B' }, + ]); + }); +}); diff --git a/src/ax/dsp/util.ts b/src/ax/dsp/util.ts index 5ae4da8c..99d56f18 100644 --- a/src/ax/dsp/util.ts +++ b/src/ax/dsp/util.ts @@ -33,6 +33,8 @@ export const validateValue = ( return val instanceof Date || typeof val === 'string'; case 'json': return typeof val === 'object' || typeof val === 'string'; + case 'object': + return typeof val === 'object'; default: return false; // Unknown or unsupported type } diff --git a/src/ax/package-exports.test.ts b/src/ax/package-exports.test.ts deleted file mode 100644 index 514c73d8..00000000 --- a/src/ax/package-exports.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { readFileSync } from 'node:fs'; -import path from 'node:path'; -import { describe, expect, it } from 'vitest'; - -describe('Package Exports Compatibility', () => { - it('should not have "default" field in exports to maintain CommonJS compatibility', () => { - // Check if dist/package.json exists, if not skip this test - const distPackageJsonPath = path.join( - process.cwd(), - 'dist', - 'package.json' - ); - try { - const packageJson = JSON.parse(readFileSync(distPackageJsonPath, 'utf8')); - - // Verify exports structure exists - expect(packageJson.exports).toBeDefined(); - expect(packageJson.exports['.']).toBeDefined(); - - // Critical: "default" field should NOT exist to maintain CommonJS compatibility - expect(packageJson.exports['.'].default).toBeUndefined(); - expect(packageJson.exports['./*']?.default).toBeUndefined(); - - // Verify required fields exist for dual compatibility - expect(packageJson.exports['.'].import).toBe('./index.js'); - expect(packageJson.exports['.'].require).toBe('./index.cjs'); - expect(packageJson.exports['.'].types).toBe('./index.d.ts'); - expect(packageJson.exports['.'].browser).toBe('./index.global.js'); - - // Verify legacy fields for older tooling - expect(packageJson.main).toBe('./index.cjs'); - expect(packageJson.module).toBe('./index.js'); - expect(packageJson.types).toBe('./index.d.ts'); - } catch (_error) { - // If dist doesn't exist, test the postbuild script configuration instead - console.warn( - 'dist/package.json not found, testing postbuild script configuration' - ); - - const postbuildPath = path.join( - process.cwd(), - '../../scripts/postbuild.js' - ); - const postbuildContent = readFileSync(postbuildPath, 'utf8'); - - // Verify the postbuild script doesn't add "default" field - expect(postbuildContent).not.toContain("default: './index.js'"); - expect(postbuildContent).not.toContain("default: './*.js'"); - } - }); - - it('should maintain proper module type configuration', async () => { - // Read the source package.json - const sourcePackageJsonPath = path.join(process.cwd(), 'package.json'); - const sourcePackageJson = JSON.parse( - readFileSync(sourcePackageJsonPath, 'utf8') - ); - - // Verify source keeps "type": "module" for development - expect(sourcePackageJson.type).toBe('module'); - }); -});