Files
united-workforce/packages/workflow-role-llm/src/create-role.ts
T
xiaoju c2a8f2d81b feat: @uncaged/workflow-role-llm — role factory + zod@4 schema
Migrated from nerve/workflow-utils:
- createRole with zod@4 schema → typed meta + JSON Schema
- createLlmAdapter — LLM provider abstraction
- llmExtract/llmExtractWithRetry — structured output extraction
- decorateRole/withDryRun/onFail — role decorators
- buildDescriptorFromRoles — auto-generate descriptor from zod schemas
- Zero nerve-core dependencies
- 83 tests pass, biome clean

Closes #9
小橘 <xiaoju@shazhou.work>
2026-05-06 06:50:19 +00:00

36 lines
1.3 KiB
TypeScript

import type { AgentFn, Role, ThreadContext } from "@uncaged/workflow";
import type * as z from "zod/v4";
import { extractMetaOrThrow } from "./llm-extract.js";
import type { LlmProvider } from "./types.js";
export type CreateRoleArgs<M extends Record<string, unknown>> = {
name: string;
schema: z.ZodType<M>;
systemPrompt: string | ((ctx: ThreadContext) => Promise<string>);
agent: AgentFn;
extract: {
provider: LlmProvider;
/** When `true`, structured extract returns schema-shaped defaults. When `null`, live API extract. */
dryRun: boolean | null;
};
};
function resolveExtractDryRun(extractDryRun: boolean | null): boolean {
return extractDryRun === true;
}
/** Builds a {@link Role} from an {@link AgentFn}, system prompt, Zod meta schema, and extract wiring. */
export function createRole<M extends Record<string, unknown>>(args: CreateRoleArgs<M>): Role<M> {
return async (ctx: ThreadContext) => {
const promptText =
typeof args.systemPrompt === "string" ? args.systemPrompt : await args.systemPrompt(ctx);
const raw = await args.agent(ctx, promptText);
const meta = await extractMetaOrThrow(args.name, raw, args.schema, {
provider: args.extract.provider,
dryRun: resolveExtractDryRun(args.extract.dryRun),
});
return { content: raw, meta };
};
}