feat: unify JSON value schema DSL
This commit is contained in:
@@ -14,7 +14,7 @@ import type { Context } from 'cordis'
|
||||
import type { ContinuationStop } from '@deepseek-ai/dsh-agent'
|
||||
import type { ContentBlock, ToolSchema } from '@deepseek-ai/dsh-llm'
|
||||
import type { ToolExecution } from '@deepseek-ai/dsh-tools'
|
||||
import { ToolArgsError, validateStructuredValue, type StructuredOutputSchema } from '@deepseek-ai/dsh-tools'
|
||||
import { ToolArgsError, validateJsonSchemaValue, type ObjectJsonSchema } from '@deepseek-ai/dsh-tools'
|
||||
|
||||
/** The model-facing tool name a structured child must call to finish. */
|
||||
export const STRUCTURED_OUTPUT_TOOL = 'structured_output'
|
||||
@@ -44,10 +44,10 @@ export interface StructuredAttachment {
|
||||
* its creation window. Child disposal removes every registration.
|
||||
* @param childCtx - the child agent's scope context (`setup`'s argument).
|
||||
* @param schema - the trusted, already-asserted schema subset to enforce (see
|
||||
* `assertSupportedOutputSchema` in dsh-tools).
|
||||
* `assertObjectJsonSchema` in dsh-tools).
|
||||
* @returns the attachment handle (read `captured()` after the child settles).
|
||||
*/
|
||||
export function attachStructuredRuntime(childCtx: Context, schema: StructuredOutputSchema): StructuredAttachment {
|
||||
export function attachStructuredRuntime(childCtx: Context, schema: ObjectJsonSchema): StructuredAttachment {
|
||||
/**
|
||||
* Validated values staged by the capture tool body, awaiting THEIR OWN
|
||||
* authoritative `tools/result` notification. The execution object's identity
|
||||
@@ -75,7 +75,7 @@ export function attachStructuredRuntime(childCtx: Context, schema: StructuredOut
|
||||
childCtx.tools.register({
|
||||
...schemaEntry,
|
||||
execute(args: unknown, exec: ToolExecution): Promise<ContentBlock[]> {
|
||||
const violations = validateStructuredValue(schema, args)
|
||||
const violations = validateJsonSchemaValue(schema, args)
|
||||
// ToolArgsError → isError result with INVALID_ARGS: the model retries
|
||||
// within the same turn, exactly like a schema-validated defineTool call.
|
||||
if (violations.length > 0) throw new ToolArgsError(violations)
|
||||
|
||||
@@ -7,7 +7,7 @@ import AgentLoop from '@deepseek-ai/dsh-agent-loop'
|
||||
import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit'
|
||||
import * as Invariants from '@deepseek-ai/dsh-invariants'
|
||||
import SubagentService, { type SubagentStartRequest } from '@deepseek-ai/dsh-subagent'
|
||||
import type { Config as ToolConfig, StructuredOutputSchema } from '@deepseek-ai/dsh-tools'
|
||||
import type { Config as ToolConfig, ObjectJsonSchema } from '@deepseek-ai/dsh-tools'
|
||||
import { RUN_CODE_NAME } from '@deepseek-ai/dsh-tools'
|
||||
import { MockAdapter, textResponse, toolCallResponse } from '../../../core/agent-loop/tests/mock-adapter.ts'
|
||||
import { startInProcessRun } from '../src/index.ts'
|
||||
@@ -27,7 +27,7 @@ interface SetupOptions {
|
||||
codeRun?: (request: CodeRunRequestLike) => Promise<{ logs: never[]; value?: unknown }>
|
||||
}
|
||||
|
||||
const SCHEMA: StructuredOutputSchema = {
|
||||
const SCHEMA: ObjectJsonSchema = {
|
||||
type: 'object',
|
||||
properties: { answer: { type: 'number' }, note: { type: 'string' } },
|
||||
required: ['answer'],
|
||||
@@ -320,17 +320,17 @@ describe('in-process structured output', () => {
|
||||
it('rejects a schema outside the subset loud, before any child exists', async () => {
|
||||
const { ctx, parent } = await setup([])
|
||||
await expect(ctx.subagents.start('spawn', structuredRequest(parent, {
|
||||
outputSchema: { type: 'object', oneOf: [] } as unknown as StructuredOutputSchema,
|
||||
}))).rejects.toThrow(/unsupported output schema/)
|
||||
outputSchema: { type: 'object', oneOf: [] } as unknown as ObjectJsonSchema,
|
||||
}))).rejects.toThrow(/unsupported JSON schema/)
|
||||
expect(ctx.agents.get(SessionId('parent'))).toBeDefined()
|
||||
})
|
||||
|
||||
it('a schema carrying non-JSON values fails as OutputSchemaError at the validation boundary', async () => {
|
||||
it('a schema carrying non-JSON values fails as JsonSchemaError at the validation boundary', async () => {
|
||||
const { ctx, parent } = await setup([])
|
||||
// Semantic assertion runs before provider startup.
|
||||
await expect(ctx.subagents.start('spawn', structuredRequest(parent, {
|
||||
outputSchema: { type: 'object', default: () => {} } as unknown as StructuredOutputSchema,
|
||||
}))).rejects.toThrow(/unsupported output schema.*annotation must be JSON data/)
|
||||
outputSchema: { type: 'object', default: () => {} } as unknown as ObjectJsonSchema,
|
||||
}))).rejects.toThrow(/unsupported JSON schema.*annotation must be lossless JSON data/)
|
||||
})
|
||||
|
||||
it('a post-execute BLOCK on the capture call denies the capture: log and result agree on failure', async () => {
|
||||
@@ -547,7 +547,7 @@ describe('in-process structured output', () => {
|
||||
})
|
||||
|
||||
it('two concurrent structured children each see their OWN schema', async () => {
|
||||
const otherSchema: StructuredOutputSchema = {
|
||||
const otherSchema: ObjectJsonSchema = {
|
||||
type: 'object',
|
||||
properties: { verdict: { type: 'string', enum: ['real', 'bogus'] } },
|
||||
required: ['verdict'],
|
||||
|
||||
@@ -32,7 +32,7 @@ import { randomUUID } from 'node:crypto'
|
||||
import { Context, Service } from 'cordis'
|
||||
import { scopeTarget } from '@deepseek-ai/dsh-scope'
|
||||
import type { Scoped } from '@deepseek-ai/dsh-scope'
|
||||
import { assertSupportedOutputSchema } from '@deepseek-ai/dsh-tools'
|
||||
import { assertObjectJsonSchema } from '@deepseek-ai/dsh-tools'
|
||||
import { HarnessError } from '@deepseek-ai/dsh-llm'
|
||||
import type { ContentBlock } from '@deepseek-ai/dsh-llm'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
@@ -242,7 +242,7 @@ export class SubagentService extends Service {
|
||||
}
|
||||
this.assertCapabilities(provider, request)
|
||||
assertSubagentMaxDepth(request.maxDepth)
|
||||
if (request.outputSchema !== undefined) assertSupportedOutputSchema(request.outputSchema)
|
||||
if (request.outputSchema !== undefined) assertObjectJsonSchema(request.outputSchema)
|
||||
|
||||
const parent = request.parent
|
||||
const run = await provider.start(request)
|
||||
|
||||
@@ -10,7 +10,7 @@ import type { Agent, AgentOptions } from '@deepseek-ai/dsh-agent'
|
||||
import type { Branded } from '@deepseek-ai/dsh-brand'
|
||||
import type { ContentBlock } from '@deepseek-ai/dsh-llm'
|
||||
import type { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import type { StructuredOutputSchema, ToolRestriction } from '@deepseek-ai/dsh-tools'
|
||||
import type { ObjectJsonSchema, ToolRestriction } from '@deepseek-ai/dsh-tools'
|
||||
|
||||
/** Identifies one accepted subagent run across its lifecycle event pair. */
|
||||
export type SubagentRunId = Branded<'SubagentRunId'>
|
||||
@@ -70,11 +70,11 @@ export interface SubagentStartRequest {
|
||||
/** Per-child agent options (model and plugin-defined extension fields). */
|
||||
readonly agentOptions?: AgentOptions
|
||||
/**
|
||||
* Object-rooted JSON Schema within `assertSupportedOutputSchema`'s enforced subset. Start rejects
|
||||
* Object-rooted JSON Schema within `assertObjectJsonSchema`'s enforced subset. Start rejects
|
||||
* unsupported schemas or providers without the capability. Data must be plain host-realm JSON;
|
||||
* a successful child returns the matching value as {@link SubagentResult.structured}.
|
||||
*/
|
||||
readonly outputSchema?: StructuredOutputSchema
|
||||
readonly outputSchema?: ObjectJsonSchema
|
||||
/**
|
||||
* Optional absolute delegation-depth cap for the child being started: its
|
||||
* computed depth must be less than or equal to this non-negative safe
|
||||
|
||||
Reference in New Issue
Block a user