refactor: extract wrapAgentAsAdapter to util-agent, support childThread in RoleFn (#222)
This commit is contained in:
+1
@@ -0,0 +1 @@
|
|||||||
|
/home/azureuser/repos/workflow/packages/workflow-cas
|
||||||
@@ -30,6 +30,7 @@ export type {
|
|||||||
Result,
|
Result,
|
||||||
RoleDefinition,
|
RoleDefinition,
|
||||||
RoleFn,
|
RoleFn,
|
||||||
|
RoleResult,
|
||||||
RoleMeta,
|
RoleMeta,
|
||||||
RoleOutput,
|
RoleOutput,
|
||||||
RoleStep,
|
RoleStep,
|
||||||
|
|||||||
@@ -157,7 +157,9 @@ export type AgentBinding = {
|
|||||||
|
|
||||||
// ── Adapter (replaces Agent) ────────────────────────────────────────
|
// ── Adapter (replaces Agent) ────────────────────────────────────────
|
||||||
|
|
||||||
export type RoleFn<T> = (ctx: ThreadContext, runtime: WorkflowRuntime) => Promise<T>;
|
export type RoleResult<T> = { meta: T; childThread: string | null };
|
||||||
|
|
||||||
|
export type RoleFn<T> = (ctx: ThreadContext, runtime: WorkflowRuntime) => Promise<RoleResult<T>>;
|
||||||
|
|
||||||
export type AdapterFn = <T>(prompt: string, schema: z.ZodType<T>) => RoleFn<T>;
|
export type AdapterFn = <T>(prompt: string, schema: z.ZodType<T>) => RoleFn<T>;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
/home/azureuser/repos/workflow/packages/workflow-protocol
|
||||||
@@ -84,7 +84,8 @@ async function advanceOneRound<M extends RoleMeta>(
|
|||||||
|
|
||||||
const adapter = adapterForRole(binding, next);
|
const adapter = adapterForRole(binding, next);
|
||||||
const roleFn = adapter(roleDef.systemPrompt, roleDef.schema as z.ZodType<Record<string, unknown>>);
|
const roleFn = adapter(roleDef.systemPrompt, roleDef.schema as z.ZodType<Record<string, unknown>>);
|
||||||
const meta = await roleFn(modCtx as unknown as ThreadContext, runtime);
|
const result = await roleFn(modCtx as unknown as ThreadContext, runtime);
|
||||||
|
const meta = result.meta;
|
||||||
|
|
||||||
const refsFromMeta = resolveExtractedRefs(
|
const refsFromMeta = resolveExtractedRefs(
|
||||||
roleDef as unknown as RoleDefinition<Record<string, unknown>>,
|
roleDef as unknown as RoleDefinition<Record<string, unknown>>,
|
||||||
@@ -110,7 +111,7 @@ async function advanceOneRound<M extends RoleMeta>(
|
|||||||
contentHash: step.contentHash,
|
contentHash: step.contentHash,
|
||||||
meta: step.meta,
|
meta: step.meta,
|
||||||
refs: step.refs,
|
refs: step.refs,
|
||||||
childThread: null,
|
childThread: result.childThread,
|
||||||
},
|
},
|
||||||
step,
|
step,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export type {
|
|||||||
Result,
|
Result,
|
||||||
RoleDefinition,
|
RoleDefinition,
|
||||||
RoleFn,
|
RoleFn,
|
||||||
|
RoleResult,
|
||||||
RoleMeta,
|
RoleMeta,
|
||||||
RoleOutput,
|
RoleOutput,
|
||||||
RoleStep,
|
RoleStep,
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export type {
|
|||||||
Result,
|
Result,
|
||||||
RoleDefinition,
|
RoleDefinition,
|
||||||
RoleFn,
|
RoleFn,
|
||||||
|
RoleResult,
|
||||||
RoleMeta,
|
RoleMeta,
|
||||||
RoleOutput,
|
RoleOutput,
|
||||||
RoleStep,
|
RoleStep,
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
/home/azureuser/repos/workflow/packages/workflow-runtime
|
||||||
@@ -4,10 +4,8 @@
|
|||||||
* All roles use cursor-agent with workspace auto-extracted from context.
|
* All roles use cursor-agent with workspace auto-extracted from context.
|
||||||
*/
|
*/
|
||||||
import { createCursorAgent } from "@uncaged/workflow-agent-cursor";
|
import { createCursorAgent } from "@uncaged/workflow-agent-cursor";
|
||||||
import { putContentNodeWithRefs } from "@uncaged/workflow-cas";
|
|
||||||
import type { AdapterFn, AgentContext, AgentFnResult, ThreadContext, WorkflowRuntime } from "@uncaged/workflow-runtime";
|
|
||||||
import { createWorkflow } from "@uncaged/workflow-runtime";
|
import { createWorkflow } from "@uncaged/workflow-runtime";
|
||||||
import type * as z from "zod/v4";
|
import { wrapAgentAsAdapter } from "@uncaged/workflow-util-agent";
|
||||||
import { buildDevelopDescriptor, developWorkflowDefinition } from "./src/index.js";
|
import { buildDevelopDescriptor, developWorkflowDefinition } from "./src/index.js";
|
||||||
|
|
||||||
function requireEnv(name: string): string {
|
function requireEnv(name: string): string {
|
||||||
@@ -43,19 +41,6 @@ const agent = createCursorAgent({
|
|||||||
llmProvider,
|
llmProvider,
|
||||||
});
|
});
|
||||||
|
|
||||||
function wrapAgentAsAdapter(agentFn: (ctx: AgentContext) => Promise<AgentFnResult>): AdapterFn {
|
|
||||||
return <T>(prompt: string, schema: z.ZodType<T>) => {
|
|
||||||
return async (ctx: ThreadContext, runtime: WorkflowRuntime): Promise<T> => {
|
|
||||||
const agentCtx: AgentContext = { ...ctx, currentRole: { name: "agent", systemPrompt: prompt } };
|
|
||||||
const result = await agentFn(agentCtx);
|
|
||||||
const output = typeof result === "string" ? result : result.output;
|
|
||||||
const contentHash = await putContentNodeWithRefs(runtime.cas, output, []);
|
|
||||||
const extracted = await runtime.extract(schema as z.ZodType<Record<string, unknown>>, contentHash);
|
|
||||||
return extracted.meta as T;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const adapter = wrapAgentAsAdapter(agent);
|
const adapter = wrapAgentAsAdapter(agent);
|
||||||
|
|
||||||
const wf = createWorkflow(developWorkflowDefinition, { adapter, overrides: null });
|
const wf = createWorkflow(developWorkflowDefinition, { adapter, overrides: null });
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
createWorkflow,
|
createWorkflow,
|
||||||
END,
|
END,
|
||||||
type ModeratorContext,
|
type ModeratorContext,
|
||||||
|
type RoleResult,
|
||||||
type RoleStep,
|
type RoleStep,
|
||||||
START,
|
START,
|
||||||
type ThreadContext,
|
type ThreadContext,
|
||||||
@@ -112,13 +113,13 @@ function makeThread(prompt: string) {
|
|||||||
function createSequenceAdapter(sequence: ReadonlyArray<Record<string, unknown>>): AdapterFn {
|
function createSequenceAdapter(sequence: ReadonlyArray<Record<string, unknown>>): AdapterFn {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
return <T>(_prompt: string, _schema: z.ZodType<T>) => {
|
return <T>(_prompt: string, _schema: z.ZodType<T>) => {
|
||||||
return async (_ctx: ThreadContext, _runtime: WorkflowRuntime): Promise<T> => {
|
return async (_ctx: ThreadContext, _runtime: WorkflowRuntime): Promise<RoleResult<T>> => {
|
||||||
const meta = sequence[i] ?? sequence[sequence.length - 1];
|
const meta = sequence[i] ?? sequence[sequence.length - 1];
|
||||||
if (meta === undefined) {
|
if (meta === undefined) {
|
||||||
throw new Error("createSequenceAdapter: empty sequence");
|
throw new Error("createSequenceAdapter: empty sequence");
|
||||||
}
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
return meta as T;
|
return { meta: meta as T, childThread: null };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -130,9 +131,9 @@ function createTrackingAdapter(
|
|||||||
meta: Record<string, unknown>,
|
meta: Record<string, unknown>,
|
||||||
): AdapterFn {
|
): AdapterFn {
|
||||||
return <T>(_prompt: string, _schema: z.ZodType<T>) => {
|
return <T>(_prompt: string, _schema: z.ZodType<T>) => {
|
||||||
return async (_ctx: ThreadContext, _runtime: WorkflowRuntime): Promise<T> => {
|
return async (_ctx: ThreadContext, _runtime: WorkflowRuntime): Promise<RoleResult<T>> => {
|
||||||
calls.push(name);
|
calls.push(name);
|
||||||
return meta as T;
|
return { meta: meta as T, childThread: null };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,9 @@
|
|||||||
* developer → workflow-as-agent (delegates to "develop" workflow)
|
* developer → workflow-as-agent (delegates to "develop" workflow)
|
||||||
*/
|
*/
|
||||||
import { createHermesAgent } from "@uncaged/workflow-agent-hermes";
|
import { createHermesAgent } from "@uncaged/workflow-agent-hermes";
|
||||||
import { putContentNodeWithRefs } from "@uncaged/workflow-cas";
|
|
||||||
import { workflowAsAgent } from "@uncaged/workflow-execute";
|
import { workflowAsAgent } from "@uncaged/workflow-execute";
|
||||||
import type { AdapterFn, AgentContext, AgentFnResult, ThreadContext, WorkflowRuntime } from "@uncaged/workflow-runtime";
|
|
||||||
import { createWorkflow } from "@uncaged/workflow-runtime";
|
import { createWorkflow } from "@uncaged/workflow-runtime";
|
||||||
import type * as z from "zod/v4";
|
import { wrapAgentAsAdapter } from "@uncaged/workflow-util-agent";
|
||||||
import { buildSolveIssueDescriptor, solveIssueWorkflowDefinition } from "./src/index.js";
|
import { buildSolveIssueDescriptor, solveIssueWorkflowDefinition } from "./src/index.js";
|
||||||
|
|
||||||
function optionalEnv(name: string): string | null {
|
function optionalEnv(name: string): string | null {
|
||||||
@@ -20,19 +18,6 @@ function optionalEnv(name: string): string | null {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function wrapAgentAsAdapter(agentFn: (ctx: AgentContext) => Promise<AgentFnResult>): AdapterFn {
|
|
||||||
return <T>(prompt: string, schema: z.ZodType<T>) => {
|
|
||||||
return async (ctx: ThreadContext, runtime: WorkflowRuntime): Promise<T> => {
|
|
||||||
const agentCtx: AgentContext = { ...ctx, currentRole: { name: "agent", systemPrompt: prompt } };
|
|
||||||
const result = await agentFn(agentCtx);
|
|
||||||
const output = typeof result === "string" ? result : result.output;
|
|
||||||
const contentHash = await putContentNodeWithRefs(runtime.cas, output, []);
|
|
||||||
const extracted = await runtime.extract(schema as z.ZodType<Record<string, unknown>>, contentHash);
|
|
||||||
return extracted.meta as T;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const hermesAgent = createHermesAgent({
|
const hermesAgent = createHermesAgent({
|
||||||
model: optionalEnv("WORKFLOW_HERMES_MODEL"),
|
model: optionalEnv("WORKFLOW_HERMES_MODEL"),
|
||||||
timeout: optionalEnv("WORKFLOW_HERMES_TIMEOUT")
|
timeout: optionalEnv("WORKFLOW_HERMES_TIMEOUT")
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
"test": "bun test"
|
"test": "bun test"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@uncaged/workflow-runtime": "workspace:*"
|
"@uncaged/workflow-runtime": "workspace:*",
|
||||||
|
"@uncaged/workflow-cas": "workspace:*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
export { buildAgentPrompt, buildThreadInput } from "./build-agent-prompt.js";
|
export { buildAgentPrompt, buildThreadInput } from "./build-agent-prompt.js";
|
||||||
export type { SpawnCliConfig, SpawnCliError, SpawnCliResult } from "./spawn-cli.js";
|
export type { SpawnCliConfig, SpawnCliError, SpawnCliResult } from "./spawn-cli.js";
|
||||||
export { spawnCli } from "./spawn-cli.js";
|
export { spawnCli } from "./spawn-cli.js";
|
||||||
|
export { wrapAgentAsAdapter } from "./wrap-agent-as-adapter.js";
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { putContentNodeWithRefs } from "@uncaged/workflow-cas";
|
||||||
|
import type {
|
||||||
|
AdapterFn,
|
||||||
|
AgentContext,
|
||||||
|
AgentFnResult,
|
||||||
|
RoleResult,
|
||||||
|
ThreadContext,
|
||||||
|
WorkflowRuntime,
|
||||||
|
} from "@uncaged/workflow-runtime";
|
||||||
|
import type * as z from "zod/v4";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps a legacy AgentFn into an AdapterFn.
|
||||||
|
* The agent produces a string (or { output, childThread }); the adapter
|
||||||
|
* stores the output in CAS, runs extract, and returns typed meta + childThread.
|
||||||
|
*/
|
||||||
|
export function wrapAgentAsAdapter(
|
||||||
|
agentFn: (ctx: AgentContext) => Promise<AgentFnResult>,
|
||||||
|
): AdapterFn {
|
||||||
|
return <T>(prompt: string, schema: z.ZodType<T>) => {
|
||||||
|
return async (ctx: ThreadContext, runtime: WorkflowRuntime): Promise<RoleResult<T>> => {
|
||||||
|
const agentCtx: AgentContext = { ...ctx, currentRole: { name: "agent", systemPrompt: prompt } };
|
||||||
|
const result = await agentFn(agentCtx);
|
||||||
|
const output = typeof result === "string" ? result : result.output;
|
||||||
|
const childThread = typeof result === "string" ? null : result.childThread;
|
||||||
|
const contentHash = await putContentNodeWithRefs(runtime.cas, output, []);
|
||||||
|
const extracted = await runtime.extract(schema as z.ZodType<Record<string, unknown>>, contentHash);
|
||||||
|
return { meta: extracted.meta as T, childThread };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -6,5 +6,5 @@
|
|||||||
"composite": true
|
"composite": true
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts"],
|
"include": ["src/**/*.ts"],
|
||||||
"references": [{ "path": "../workflow-runtime" }]
|
"references": [{ "path": "../workflow-runtime" }, { "path": "../workflow-cas" }]
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
/home/azureuser/repos/workflow/packages/workflow-util
|
||||||
Reference in New Issue
Block a user