Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/runner.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ZodError } from 'zod'
import type { FailureContext, Failure, Gate, Result, Step } from './types.js'
import type { Failure, FailureContext, Gate, Result, Step } from './types.js'

/**
* Format a ZodError into a human-readable reason string.
Expand All @@ -17,7 +17,10 @@ function formatZodError(error: ZodError): string {
* Run quality gates against a step's output.
* Returns the first failure, or null if all gates pass.
*/
function runGates(gates: Gate<unknown>[], output: unknown): { reason: string; context?: unknown } | null {
function runGates(
gates: Gate<unknown>[],
output: unknown,
): { reason: string; context?: unknown } | null {
for (const gate of gates) {
const result = gate(output)
if (!result.ok) {
Expand Down
8 changes: 2 additions & 6 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import type { z } from 'zod'
* Result of a quality gate check.
* Gates are pure synchronous functions — no LLM calls, no async.
*/
export type GateResult =
| { ok: true }
| { ok: false; reason: string; context?: unknown }
export type GateResult = { ok: true } | { ok: false; reason: string; context?: unknown }

/**
* A quality gate function. Receives a step's validated output
Expand Down Expand Up @@ -83,9 +81,7 @@ export interface Failure {
/**
* Pipeline result type. Success or structured failure.
*/
export type Result<T> =
| { ok: true; value: T }
| { ok: false; failure: Failure }
export type Result<T> = { ok: true; value: T } | { ok: false; failure: Failure }

/**
* Metadata returned by pipeline.describe().
Expand Down
2 changes: 1 addition & 1 deletion tests/failure.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest'
import { z } from 'zod'
import { defineStep, pipeline, PipelineError } from '../src/index.js'
import { PipelineError, defineStep, pipeline } from '../src/index.js'

describe('structured failures', () => {
it('schema_input failure has correct shape', async () => {
Expand Down
4 changes: 1 addition & 3 deletions tests/gates.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,7 @@ describe('quality gates', () => {
gates: [
(out) => (out.text.length > 0 ? { ok: true } : { ok: false, reason: 'empty' }),
(out) =>
out.text.length < 10
? { ok: true }
: { ok: false, reason: 'too long (max 10 chars)' },
out.text.length < 10 ? { ok: true } : { ok: false, reason: 'too long (max 10 chars)' },
(_out) => ({ ok: false, reason: 'this gate should not run' }),
],
run: async (input) => ({ text: input.text.repeat(5) }),
Expand Down
20 changes: 10 additions & 10 deletions tests/pipeline.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest'
import { z } from 'zod'
import { defineStep, pipeline, PipelineError } from '../src/index.js'
import { PipelineError, defineStep, pipeline } from '../src/index.js'

const double = defineStep({
name: 'double',
Expand All @@ -9,7 +9,7 @@ const double = defineStep({
run: async (input) => ({ value: input.value * 2 }),
})

const toString = defineStep({
const toText = defineStep({
name: 'to-string',
input: z.object({ value: z.number() }),
output: z.object({ text: z.string() }),
Expand All @@ -28,7 +28,7 @@ describe('pipeline', () => {
})

it('chains multiple steps', async () => {
const p = pipeline('multi').step(double).step(toString)
const p = pipeline('multi').step(double).step(toText)
const result = await p.run({ value: 21 })

expect(result.ok).toBe(true)
Expand All @@ -54,7 +54,7 @@ describe('pipeline', () => {
name: 'bad-output',
input: z.object({ x: z.number() }),
output: z.object({ y: z.string() }),
run: async (input) => ({ y: input.x } as unknown as { y: string }),
run: async (input) => ({ y: input.x }) as unknown as { y: string },
})

const p = pipeline('bad-output').step(badStep)
Expand Down Expand Up @@ -117,15 +117,15 @@ describe('pipeline', () => {
})

it('describe() returns pipeline metadata', () => {
const p = pipeline('described').step(double).step(toString)
const p = pipeline('described').step(double).step(toText)
const desc = p.describe()

expect(desc.name).toBe('described')
expect(desc.steps).toHaveLength(2)
expect(desc.steps[0]!.name).toBe('double')
expect(desc.steps[1]!.name).toBe('to-string')
expect(desc.steps[0]!.inputSchema).toHaveProperty('type', 'object')
expect(desc.steps[0]!.outputSchema).toHaveProperty('type', 'object')
expect(desc.steps[0]!.gates).toBe(0)
expect(desc.steps[0]?.name).toBe('double')
expect(desc.steps[1]?.name).toBe('to-string')
expect(desc.steps[0]?.inputSchema).toHaveProperty('type', 'object')
expect(desc.steps[0]?.outputSchema).toHaveProperty('type', 'object')
expect(desc.steps[0]?.gates).toBe(0)
})
})
6 changes: 3 additions & 3 deletions tests/retry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ describe('retry with failure context', () => {
expect(contexts).toHaveLength(2)
expect(contexts[0]).toBeUndefined() // first attempt
expect(contexts[1]).toBeDefined() // second attempt has context
expect(contexts[1]!.type).toBe('gate')
expect(contexts[1]!.attempt).toBe(1)
expect(contexts[1]!.reason).toContain('title too long')
expect(contexts[1]?.type).toBe('gate')
expect(contexts[1]?.attempt).toBe(1)
expect(contexts[1]?.reason).toContain('title too long')
})

it('exhausts retries and returns structured failure', async () => {
Expand Down
Loading