@openapi-flow/langchain 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +32 -2
- package/THIRD_PARTY_NOTICES.md +3 -0
- package/dist/arguments/generate-api-arguments.d.ts +3 -0
- package/dist/arguments/generate-api-arguments.js +45 -0
- package/dist/arguments/select-api-request-media-type.d.ts +2 -0
- package/dist/arguments/select-api-request-media-type.js +39 -0
- package/dist/bindings/plan-api-bindings.d.ts +3 -0
- package/dist/bindings/plan-api-bindings.js +43 -0
- package/dist/legacy/planning/operation-plan-inputs.d.ts +3 -0
- package/dist/legacy/planning/operation-plan-inputs.js +17 -0
- package/dist/legacy/planning/parse-structured-output.d.ts +2 -0
- package/dist/legacy/planning/parse-structured-output.js +9 -0
- package/dist/legacy/planning/propose-catalog-scenario.d.ts +3 -0
- package/dist/legacy/planning/propose-catalog-scenario.js +136 -0
- package/dist/legacy/planning/select-operation.d.ts +4 -0
- package/dist/legacy/planning/select-operation.js +43 -0
- package/dist/legacy/schemas/catalog-selection-schema.d.ts +19 -0
- package/dist/legacy/schemas/catalog-selection-schema.js +39 -0
- package/dist/legacy/schemas/catalog-step-schema.d.ts +16 -0
- package/dist/legacy/schemas/catalog-step-schema.js +39 -0
- package/dist/legacy/schemas/operation-selection-schema.d.ts +6 -0
- package/dist/legacy/schemas/operation-selection-schema.js +14 -0
- package/dist/legacy-api.d.ts +9 -0
- package/dist/legacy-api.js +7 -0
- package/dist/prompts/api-arguments-prompt.d.ts +1 -0
- package/dist/prompts/api-arguments-prompt.js +1 -0
- package/dist/prompts/api-bindings-prompt.d.ts +1 -0
- package/dist/prompts/api-bindings-prompt.js +12 -0
- package/dist/prompts/api-request-media-type-prompt.d.ts +1 -0
- package/dist/prompts/api-request-media-type-prompt.js +1 -0
- package/dist/prompts/native-node-planning-prompt.d.ts +1 -0
- package/dist/prompts/native-node-planning-prompt.js +9 -0
- package/dist/prompts/operation-selection-prompt.d.ts +1 -0
- package/dist/prompts/operation-selection-prompt.js +1 -0
- package/dist/prompts/reviewable-workflow-graph-prompt.d.ts +1 -0
- package/dist/prompts/reviewable-workflow-graph-prompt.js +7 -0
- package/dist/prompts/workflow-connections-prompt.d.ts +1 -0
- package/dist/prompts/workflow-connections-prompt.js +9 -0
- package/dist/prompts/workflow-graph-prompt.d.ts +1 -0
- package/dist/prompts/workflow-graph-prompt.js +13 -0
- package/dist/public-api.d.ts +20 -0
- package/dist/public-api.js +12 -0
- package/dist/schemas/api-binding-plan-schema.d.ts +3 -0
- package/dist/schemas/api-binding-plan-schema.js +49 -0
- package/dist/schemas/api-request-media-type-schema.d.ts +3 -0
- package/dist/schemas/api-request-media-type-schema.js +11 -0
- package/dist/schemas/api-selection-schema.d.ts +12 -0
- package/dist/schemas/api-selection-schema.js +30 -0
- package/dist/schemas/native-node-plan-schema.d.ts +4 -0
- package/dist/schemas/native-node-plan-schema.js +26 -0
- package/dist/schemas/reviewable-workflow-graph-schema.d.ts +4 -0
- package/dist/schemas/reviewable-workflow-graph-schema.js +63 -0
- package/dist/schemas/workflow-graph-proposal-schema.d.ts +4 -0
- package/dist/schemas/workflow-graph-proposal-schema.js +38 -0
- package/dist/selection/select-api-operations.d.ts +3 -0
- package/dist/selection/select-api-operations.js +51 -0
- package/dist/types/argument-generation.d.ts +7 -0
- package/dist/types/argument-generation.js +1 -0
- package/dist/types/binding-planning.d.ts +8 -0
- package/dist/types/binding-planning.js +1 -0
- package/dist/types/legacy/catalog-planning.d.ts +16 -0
- package/dist/types/legacy/catalog-planning.js +1 -0
- package/dist/types/legacy/planning.d.ts +5 -0
- package/dist/types/legacy/planning.js +1 -0
- package/dist/types/native-node-planning.d.ts +25 -0
- package/dist/types/native-node-planning.js +1 -0
- package/dist/types/operation-selection.d.ts +10 -0
- package/dist/types/operation-selection.js +1 -0
- package/dist/types/request-media-selection.d.ts +10 -0
- package/dist/types/request-media-selection.js +1 -0
- package/dist/types/reviewable-workflow-planning.d.ts +19 -0
- package/dist/types/reviewable-workflow-planning.js +1 -0
- package/dist/types/workflow-orchestration.d.ts +23 -0
- package/dist/types/workflow-orchestration.js +1 -0
- package/dist/types/workflow-planning.d.ts +23 -0
- package/dist/types/workflow-planning.js +1 -0
- package/dist/workflow/assemble-native-node-materials.d.ts +3 -0
- package/dist/workflow/assemble-native-node-materials.js +8 -0
- package/dist/workflow/complete-reviewable-workflow-graph-proposal.d.ts +4 -0
- package/dist/workflow/complete-reviewable-workflow-graph-proposal.js +13 -0
- package/dist/workflow/create-prepared-native-node-materials.d.ts +4 -0
- package/dist/workflow/create-prepared-native-node-materials.js +15 -0
- package/dist/workflow/native-node-planning-error.d.ts +5 -0
- package/dist/workflow/native-node-planning-error.js +10 -0
- package/dist/workflow/orchestrate-workflow.d.ts +4 -0
- package/dist/workflow/orchestrate-workflow.js +31 -0
- package/dist/workflow/plan-native-nodes.d.ts +2 -0
- package/dist/workflow/plan-native-nodes.js +99 -0
- package/dist/workflow/plan-reviewable-workflow-graph.d.ts +3 -0
- package/dist/workflow/plan-reviewable-workflow-graph.js +101 -0
- package/dist/workflow/plan-workflow-connections.d.ts +3 -0
- package/dist/workflow/plan-workflow-connections.js +62 -0
- package/dist/workflow/plan-workflow-graph.d.ts +3 -0
- package/dist/workflow/plan-workflow-graph.js +99 -0
- package/dist/workflow/prepare-workflow-connection-materials.d.ts +3 -0
- package/dist/workflow/prepare-workflow-connection-materials.js +66 -0
- package/dist/workflow/reviewable-workflow-graph-planning-error.d.ts +5 -0
- package/dist/workflow/reviewable-workflow-graph-planning-error.js +8 -0
- package/dist/workflow/workflow-graph-planning-error.d.ts +5 -0
- package/dist/workflow/workflow-graph-planning-error.js +10 -0
- package/package.json +44 -4
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
2
|
+
import type { ReviewableWorkflowGraphProposal, ReviewableWorkflowPlanningContext, WorkflowReviewGap, PlannedNativeNode } from '@openapi-flow/core';
|
|
3
|
+
export interface PlanReviewableWorkflowGraphInput extends ReviewableWorkflowPlanningContext {
|
|
4
|
+
scenario: string;
|
|
5
|
+
model: BaseChatModel;
|
|
6
|
+
gaps: WorkflowReviewGap[];
|
|
7
|
+
preparedNativeNodes?: PlannedNativeNode[];
|
|
8
|
+
}
|
|
9
|
+
export type ReviewableWorkflowGraphPlanningFailure = {
|
|
10
|
+
stage: 'proposal-schema';
|
|
11
|
+
output: unknown;
|
|
12
|
+
} | {
|
|
13
|
+
stage: 'graph-validation';
|
|
14
|
+
output: ReviewableWorkflowGraphOutput;
|
|
15
|
+
};
|
|
16
|
+
export type ReviewableWorkflowGraphOutput = Omit<ReviewableWorkflowGraphProposal, 'nativeNodes' | 'blockedCalls'> & {
|
|
17
|
+
additionalNativeNodes?: ReviewableWorkflowGraphProposal['nativeNodes'];
|
|
18
|
+
blockedCalls?: ReviewableWorkflowGraphProposal['blockedCalls'];
|
|
19
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
2
|
+
import type { ReviewableWorkflowApiMaterial, PlannedNativeNode, WorkflowCapability, WorkflowPlanEdge, WorkflowReviewGap, WorkflowPlanNodeContract } from '@openapi-flow/core';
|
|
3
|
+
export interface PlanWorkflowConnectionsInput {
|
|
4
|
+
scenario: string;
|
|
5
|
+
model: BaseChatModel;
|
|
6
|
+
materials?: ReviewableWorkflowApiMaterial[];
|
|
7
|
+
capabilities?: WorkflowCapability[];
|
|
8
|
+
nativeNodes?: PlannedNativeNode[];
|
|
9
|
+
gaps?: WorkflowReviewGap[];
|
|
10
|
+
edges?: WorkflowPlanEdge[];
|
|
11
|
+
}
|
|
12
|
+
export interface OrchestrateWorkflowInput extends Omit<PlanWorkflowConnectionsInput, 'nativeNodes'> {
|
|
13
|
+
workflowId: string;
|
|
14
|
+
preparedNativeNodes?: PlannedNativeNode[];
|
|
15
|
+
}
|
|
16
|
+
export interface WorkflowConnectionMaterials {
|
|
17
|
+
materials: ReviewableWorkflowApiMaterial[];
|
|
18
|
+
capabilities: WorkflowCapability[];
|
|
19
|
+
nativeNodes: PlannedNativeNode[];
|
|
20
|
+
gaps: WorkflowReviewGap[];
|
|
21
|
+
edges: WorkflowPlanEdge[];
|
|
22
|
+
contracts: WorkflowPlanNodeContract[];
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
2
|
+
import type { WorkflowApiMaterial, WorkflowCapability, WorkflowGraphProposal, PlannedNativeNode } from '@openapi-flow/core';
|
|
3
|
+
export interface PlanWorkflowGraphInput {
|
|
4
|
+
scenario: string;
|
|
5
|
+
model: BaseChatModel;
|
|
6
|
+
materials: WorkflowApiMaterial[];
|
|
7
|
+
capabilities: WorkflowCapability[];
|
|
8
|
+
preparedNativeNodes?: PlannedNativeNode[];
|
|
9
|
+
}
|
|
10
|
+
export interface PreparedNativeNodeMaterial extends PlannedNativeNode {
|
|
11
|
+
inputPorts: string[];
|
|
12
|
+
outputPorts: string[];
|
|
13
|
+
}
|
|
14
|
+
export type WorkflowGraphOutput = Omit<WorkflowGraphProposal, 'nativeNodes'> & {
|
|
15
|
+
additionalNativeNodes: PlannedNativeNode[];
|
|
16
|
+
};
|
|
17
|
+
export type WorkflowGraphPlanningFailure = {
|
|
18
|
+
stage: 'proposal-schema';
|
|
19
|
+
output: unknown;
|
|
20
|
+
} | {
|
|
21
|
+
stage: 'graph-validation';
|
|
22
|
+
output: WorkflowGraphOutput;
|
|
23
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { PlannedNativeNode } from '@openapi-flow/core';
|
|
2
|
+
/** Prepared values belong to the host; model output contains only additions. */
|
|
3
|
+
export declare function assembleNativeNodeMaterials(prepared: PlannedNativeNode[], additional: PlannedNativeNode[]): PlannedNativeNode[];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Prepared values belong to the host; model output contains only additions. */
|
|
2
|
+
export function assembleNativeNodeMaterials(prepared, additional) {
|
|
3
|
+
const preparedIds = new Set(prepared.map((node) => node.id));
|
|
4
|
+
for (const node of additional)
|
|
5
|
+
if (preparedIds.has(node.id))
|
|
6
|
+
throw new Error(`additionalNativeNodes[${node.id}] duplicates preparedNativeNodes; return only new nodes, not existing materials`);
|
|
7
|
+
return [...prepared, ...additional];
|
|
8
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ReviewableWorkflowGraphProposal } from '@openapi-flow/core';
|
|
2
|
+
import type { ReviewableWorkflowGraphOutput } from '../types/reviewable-workflow-planning.js';
|
|
3
|
+
/** Complete only fields fixed by the supplied planning context, not model errors. */
|
|
4
|
+
export declare function completeReviewableWorkflowGraphProposal(output: ReviewableWorkflowGraphOutput, capabilityCount: number, readyCallCount: number): ReviewableWorkflowGraphProposal;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Complete only fields fixed by the supplied planning context, not model errors. */
|
|
2
|
+
export function completeReviewableWorkflowGraphProposal(output, capabilityCount, readyCallCount) {
|
|
3
|
+
if (capabilityCount > 0 && output.additionalNativeNodes === undefined)
|
|
4
|
+
throw new Error('model reviewable workflow graph.additionalNativeNodes is required');
|
|
5
|
+
if (readyCallCount > 0 && output.blockedCalls === undefined)
|
|
6
|
+
throw new Error('model reviewable workflow graph.blockedCalls is required');
|
|
7
|
+
const { additionalNativeNodes, ...proposal } = output;
|
|
8
|
+
return {
|
|
9
|
+
...proposal,
|
|
10
|
+
nativeNodes: capabilityCount === 0 ? [] : additionalNativeNodes,
|
|
11
|
+
blockedCalls: readyCallCount === 0 ? [] : output.blockedCalls,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { PlannedNativeNode, WorkflowCapability } from '@openapi-flow/core';
|
|
2
|
+
import type { PreparedNativeNodeMaterial } from '../types/workflow-planning.js';
|
|
3
|
+
/** Expose existing implementation-owned ports, not descriptions for the model to guess. */
|
|
4
|
+
export declare function createPreparedNativeNodeMaterials(nodes: PlannedNativeNode[], capabilities: WorkflowCapability[]): PreparedNativeNodeMaterial[];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { parseNativeNodeParameters } from '@openapi-flow/core';
|
|
2
|
+
/** Expose existing implementation-owned ports, not descriptions for the model to guess. */
|
|
3
|
+
export function createPreparedNativeNodeMaterials(nodes, capabilities) {
|
|
4
|
+
return nodes.map((planned) => {
|
|
5
|
+
const capability = capabilities.find((item) => item.name === planned.capability);
|
|
6
|
+
if (!capability)
|
|
7
|
+
throw new Error(`prepared node ${planned.id}: unknown capability ${planned.capability}`);
|
|
8
|
+
const parameters = parseNativeNodeParameters(planned, capability);
|
|
9
|
+
return {
|
|
10
|
+
...planned,
|
|
11
|
+
inputPorts: capability.inputPorts(parameters),
|
|
12
|
+
outputPorts: capability.outputPorts(parameters),
|
|
13
|
+
};
|
|
14
|
+
});
|
|
15
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export class NativeNodePlanningError extends Error {
|
|
2
|
+
failure;
|
|
3
|
+
constructor(failure, cause) {
|
|
4
|
+
super(`Native node planning failed at ${failure.stage}: ${cause.message}`, {
|
|
5
|
+
cause,
|
|
6
|
+
});
|
|
7
|
+
this.name = 'NativeNodePlanningError';
|
|
8
|
+
this.failure = failure;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ReviewableWorkflowGraphPlan } from '@openapi-flow/core';
|
|
2
|
+
import type { OrchestrateWorkflowInput } from '../types/workflow-orchestration.js';
|
|
3
|
+
/** Optional material groups mean absence, never an implicit capability registry. */
|
|
4
|
+
export declare function orchestrateWorkflow(input: OrchestrateWorkflowInput): Promise<ReviewableWorkflowGraphPlan>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { planNativeNodes } from './plan-native-nodes.js';
|
|
2
|
+
import { planWorkflowConnections } from './plan-workflow-connections.js';
|
|
3
|
+
import { assembleNativeNodeMaterials } from './assemble-native-node-materials.js';
|
|
4
|
+
import { prepareWorkflowConnectionMaterials } from './prepare-workflow-connection-materials.js';
|
|
5
|
+
/** Optional material groups mean absence, never an implicit capability registry. */
|
|
6
|
+
export async function orchestrateWorkflow(input) {
|
|
7
|
+
if (!input.workflowId?.trim())
|
|
8
|
+
throw new Error('orchestrateWorkflow.workflowId is required');
|
|
9
|
+
prepareWorkflowConnectionMaterials({
|
|
10
|
+
...input,
|
|
11
|
+
nativeNodes: input.preparedNativeNodes,
|
|
12
|
+
});
|
|
13
|
+
const nativePlan = await planNativeNodes({
|
|
14
|
+
planId: `${input.workflowId}-native`,
|
|
15
|
+
scenario: input.scenario,
|
|
16
|
+
model: input.model,
|
|
17
|
+
capabilities: input.capabilities,
|
|
18
|
+
preparedNativeNodes: input.preparedNativeNodes,
|
|
19
|
+
apiMaterials: input.materials?.map((material) => ({
|
|
20
|
+
callId: material.status === 'ready'
|
|
21
|
+
? material.arguments.callId
|
|
22
|
+
: material.callId,
|
|
23
|
+
operation: material.operation,
|
|
24
|
+
})),
|
|
25
|
+
});
|
|
26
|
+
return planWorkflowConnections({
|
|
27
|
+
...input,
|
|
28
|
+
nativeNodes: assembleNativeNodeMaterials(input.preparedNativeNodes ?? [], nativePlan.nativeNodes),
|
|
29
|
+
gaps: [...(input.gaps ?? []), ...nativePlan.gaps],
|
|
30
|
+
});
|
|
31
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { HumanMessage, SystemMessage } from '@langchain/core/messages';
|
|
2
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { createNativeOutputContracts, createWorkflowPlanningCapabilities, validateNativeApiResponseReferences, } from '@openapi-flow/core';
|
|
5
|
+
import { createNativeNodePlanSchema } from '../schemas/native-node-plan-schema.js';
|
|
6
|
+
import { nativeNodePlanningPrompt } from '../prompts/native-node-planning-prompt.js';
|
|
7
|
+
import { assertSelectionInput } from '../legacy/planning/select-operation.js';
|
|
8
|
+
import { parseStructuredOutput } from '../legacy/planning/parse-structured-output.js';
|
|
9
|
+
import { createPreparedNativeNodeMaterials } from './create-prepared-native-node-materials.js';
|
|
10
|
+
import { assembleNativeNodeMaterials } from './assemble-native-node-materials.js';
|
|
11
|
+
import { NativeNodePlanningError } from './native-node-planning-error.js';
|
|
12
|
+
export async function planNativeNodes(input) {
|
|
13
|
+
assertSelectionInput(input.scenario, input.model);
|
|
14
|
+
if (!input.planId?.trim())
|
|
15
|
+
throw new Error('planNativeNodes.planId is required');
|
|
16
|
+
const capabilities = input.capabilities ?? [];
|
|
17
|
+
const apiMaterials = input.apiMaterials ?? [];
|
|
18
|
+
const prepared = input.preparedNativeNodes ?? [];
|
|
19
|
+
createNativeOutputContracts({ nativeNodes: prepared, capabilities });
|
|
20
|
+
const apiIds = apiMaterials.map((material) => material.callId);
|
|
21
|
+
const existingIds = [...apiIds, ...prepared.map((node) => node.id)];
|
|
22
|
+
if (existingIds.some((id) => !id.trim()) ||
|
|
23
|
+
new Set(existingIds).size !== existingIds.length)
|
|
24
|
+
throw new Error('native planning materials require unique non-empty IDs');
|
|
25
|
+
validateNativeApiResponseReferences({
|
|
26
|
+
nativeNodes: prepared,
|
|
27
|
+
capabilities,
|
|
28
|
+
apiCallIds: apiIds,
|
|
29
|
+
});
|
|
30
|
+
// No registered choice means no native-selection model call, not an inferred registry.
|
|
31
|
+
if (!capabilities.length)
|
|
32
|
+
return { nativeNodes: [], gaps: [] };
|
|
33
|
+
const planningCapabilities = createWorkflowPlanningCapabilities({
|
|
34
|
+
capabilities,
|
|
35
|
+
apiCallIds: apiIds,
|
|
36
|
+
});
|
|
37
|
+
const schema = createNativeNodePlanSchema(planningCapabilities);
|
|
38
|
+
const output = await input.model
|
|
39
|
+
.withStructuredOutput(z.toJSONSchema(schema), {
|
|
40
|
+
name: 'plan_native_nodes',
|
|
41
|
+
method: 'functionCalling',
|
|
42
|
+
strict: true,
|
|
43
|
+
})
|
|
44
|
+
.invoke([
|
|
45
|
+
new SystemMessage(nativeNodePlanningPrompt),
|
|
46
|
+
new HumanMessage(JSON.stringify({
|
|
47
|
+
scenario: input.scenario,
|
|
48
|
+
apiMaterials: apiMaterials.map(({ callId, operation }) => ({
|
|
49
|
+
callId,
|
|
50
|
+
key: operation.key,
|
|
51
|
+
method: operation.method,
|
|
52
|
+
path: operation.path,
|
|
53
|
+
operation: operation.operation,
|
|
54
|
+
effective: operation.effective,
|
|
55
|
+
})),
|
|
56
|
+
preparedNativeNodes: createPreparedNativeNodeMaterials(prepared, capabilities),
|
|
57
|
+
capabilities: planningCapabilities.map((capability) => ({
|
|
58
|
+
name: capability.name,
|
|
59
|
+
description: capability.description,
|
|
60
|
+
parametersSchema: z.toJSONSchema(capability.parametersSchema),
|
|
61
|
+
})),
|
|
62
|
+
})),
|
|
63
|
+
]);
|
|
64
|
+
let proposal;
|
|
65
|
+
try {
|
|
66
|
+
proposal = parseStructuredOutput(schema, output, 'model native node plan');
|
|
67
|
+
if (!isDeepStrictEqual(proposal, output))
|
|
68
|
+
throw new Error('native model schema must validate without defaults, coercion or transformation');
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (!(error instanceof Error))
|
|
72
|
+
throw error;
|
|
73
|
+
throw new NativeNodePlanningError({ stage: 'proposal-schema', output }, error);
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
const nodes = assembleNativeNodeMaterials(prepared, proposal.nativeNodes);
|
|
77
|
+
if (proposal.nativeNodes.some((node) => apiIds.includes(node.id)))
|
|
78
|
+
throw new Error('native node plan contains an API callId');
|
|
79
|
+
createNativeOutputContracts({ nativeNodes: nodes, capabilities });
|
|
80
|
+
validateNativeApiResponseReferences({
|
|
81
|
+
nativeNodes: nodes,
|
|
82
|
+
capabilities,
|
|
83
|
+
apiCallIds: apiIds,
|
|
84
|
+
});
|
|
85
|
+
const gaps = proposal.gaps.map((gap, index) => ({
|
|
86
|
+
id: `${input.planId}-gap-${index + 1}`,
|
|
87
|
+
stage: 'workflow-graph',
|
|
88
|
+
description: gap.description,
|
|
89
|
+
}));
|
|
90
|
+
if (gaps.some((gap) => [...existingIds, ...nodes.map((node) => node.id)].includes(gap.id)))
|
|
91
|
+
throw new Error('native planning gap ID collides with a declared node');
|
|
92
|
+
return { nativeNodes: proposal.nativeNodes, gaps };
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
if (!(error instanceof Error))
|
|
96
|
+
throw error;
|
|
97
|
+
throw new NativeNodePlanningError({ stage: 'node-validation', output: proposal }, error);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ReviewableWorkflowGraphPlan } from '@openapi-flow/core';
|
|
2
|
+
import type { PlanReviewableWorkflowGraphInput } from '../types/reviewable-workflow-planning.js';
|
|
3
|
+
export declare function planReviewableWorkflowGraph(input: PlanReviewableWorkflowGraphInput): Promise<ReviewableWorkflowGraphPlan>;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { HumanMessage, SystemMessage } from '@langchain/core/messages';
|
|
2
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { createReviewableWorkflowGraphPlan, createNativeOutputContracts, createWorkflowPlanningCapabilities, validateNativeApiResponseReferences, } from '@openapi-flow/core';
|
|
5
|
+
import { completeReviewableWorkflowGraphProposal } from './complete-reviewable-workflow-graph-proposal.js';
|
|
6
|
+
import { createReviewableWorkflowGraphSchema } from '../schemas/reviewable-workflow-graph-schema.js';
|
|
7
|
+
import { reviewableWorkflowGraphPrompt } from '../prompts/reviewable-workflow-graph-prompt.js';
|
|
8
|
+
import { assertSelectionInput } from '../legacy/planning/select-operation.js';
|
|
9
|
+
import { parseStructuredOutput } from '../legacy/planning/parse-structured-output.js';
|
|
10
|
+
import { ReviewableWorkflowGraphPlanningError } from './reviewable-workflow-graph-planning-error.js';
|
|
11
|
+
import { createPreparedNativeNodeMaterials } from './create-prepared-native-node-materials.js';
|
|
12
|
+
import { assembleNativeNodeMaterials } from './assemble-native-node-materials.js';
|
|
13
|
+
export async function planReviewableWorkflowGraph(input) {
|
|
14
|
+
assertSelectionInput(input.scenario, input.model);
|
|
15
|
+
const preparedNativeNodes = input.preparedNativeNodes ?? [];
|
|
16
|
+
const apiCallIds = input.materials.map((material) => material.status === 'ready' ? material.arguments.callId : material.callId);
|
|
17
|
+
const planningCapabilities = createWorkflowPlanningCapabilities({
|
|
18
|
+
capabilities: input.capabilities,
|
|
19
|
+
apiCallIds,
|
|
20
|
+
});
|
|
21
|
+
validateNativeApiResponseReferences({
|
|
22
|
+
nativeNodes: preparedNativeNodes,
|
|
23
|
+
capabilities: input.capabilities,
|
|
24
|
+
apiCallIds,
|
|
25
|
+
});
|
|
26
|
+
const nativeOutputs = createNativeOutputContracts({
|
|
27
|
+
nativeNodes: preparedNativeNodes,
|
|
28
|
+
capabilities: input.capabilities,
|
|
29
|
+
});
|
|
30
|
+
const readyIds = input.materials
|
|
31
|
+
.filter((item) => item.status === 'ready')
|
|
32
|
+
.map((item) => item.arguments.callId);
|
|
33
|
+
const schema = createReviewableWorkflowGraphSchema(planningCapabilities, readyIds);
|
|
34
|
+
const output = await input.model
|
|
35
|
+
.withStructuredOutput(schema, {
|
|
36
|
+
name: 'plan_reviewable_workflow_graph',
|
|
37
|
+
method: 'functionCalling',
|
|
38
|
+
strict: true,
|
|
39
|
+
})
|
|
40
|
+
.invoke([
|
|
41
|
+
new SystemMessage(reviewableWorkflowGraphPrompt),
|
|
42
|
+
new HumanMessage(JSON.stringify({
|
|
43
|
+
scenario: input.scenario,
|
|
44
|
+
preparedNativeNodes: createPreparedNativeNodeMaterials(preparedNativeNodes, input.capabilities),
|
|
45
|
+
nativeOutputs,
|
|
46
|
+
materials: input.materials.map(({ operation, ...material }) => ({
|
|
47
|
+
...material,
|
|
48
|
+
operation: {
|
|
49
|
+
key: operation.key,
|
|
50
|
+
method: operation.method,
|
|
51
|
+
path: operation.path,
|
|
52
|
+
operation: {
|
|
53
|
+
summary: operation.operation.summary,
|
|
54
|
+
description: operation.operation.description,
|
|
55
|
+
responses: operation.operation.responses,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
inputPorts: ['main'],
|
|
59
|
+
outputPorts: ['main'],
|
|
60
|
+
})),
|
|
61
|
+
gaps: input.gaps.map((gap) => ({
|
|
62
|
+
...gap,
|
|
63
|
+
inputPorts: ['main'],
|
|
64
|
+
outputPorts: ['main'],
|
|
65
|
+
})),
|
|
66
|
+
capabilities: planningCapabilities.map((capability) => ({
|
|
67
|
+
name: capability.name,
|
|
68
|
+
description: capability.description,
|
|
69
|
+
parametersSchema: z.toJSONSchema(capability.parametersSchema),
|
|
70
|
+
waitsForAllInputs: capability.waitsForAllInputs,
|
|
71
|
+
exclusiveOutputPorts: capability.exclusiveOutputPorts,
|
|
72
|
+
})),
|
|
73
|
+
})),
|
|
74
|
+
]);
|
|
75
|
+
let proposal;
|
|
76
|
+
try {
|
|
77
|
+
const parsed = parseStructuredOutput(schema, output, 'model reviewable workflow graph');
|
|
78
|
+
if (!isDeepStrictEqual(parsed, output))
|
|
79
|
+
throw new Error('native model schema must validate without defaults, coercion or transformation');
|
|
80
|
+
proposal = completeReviewableWorkflowGraphProposal(parsed, input.capabilities.length, readyIds.length);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
if (!(error instanceof Error))
|
|
84
|
+
throw error;
|
|
85
|
+
throw new ReviewableWorkflowGraphPlanningError({ stage: 'proposal-schema', output }, error);
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
return createReviewableWorkflowGraphPlan({
|
|
89
|
+
...input,
|
|
90
|
+
proposal: {
|
|
91
|
+
...proposal,
|
|
92
|
+
nativeNodes: assembleNativeNodeMaterials(preparedNativeNodes, proposal.nativeNodes),
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
if (!(error instanceof Error))
|
|
98
|
+
throw error;
|
|
99
|
+
throw new ReviewableWorkflowGraphPlanningError({ stage: 'graph-validation', output }, error);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ReviewableWorkflowGraphPlan } from '@openapi-flow/core';
|
|
2
|
+
import type { PlanWorkflowConnectionsInput } from '../types/workflow-orchestration.js';
|
|
3
|
+
export declare function planWorkflowConnections(input: PlanWorkflowConnectionsInput): Promise<ReviewableWorkflowGraphPlan>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { HumanMessage, SystemMessage } from '@langchain/core/messages';
|
|
2
|
+
import { createReviewableWorkflowGraphPlan } from '@openapi-flow/core';
|
|
3
|
+
import { createReviewableWorkflowGraphSchema } from '../schemas/reviewable-workflow-graph-schema.js';
|
|
4
|
+
import { completeReviewableWorkflowGraphProposal } from './complete-reviewable-workflow-graph-proposal.js';
|
|
5
|
+
import { workflowConnectionsPrompt } from '../prompts/workflow-connections-prompt.js';
|
|
6
|
+
import { assertSelectionInput } from '../legacy/planning/select-operation.js';
|
|
7
|
+
import { parseStructuredOutput } from '../legacy/planning/parse-structured-output.js';
|
|
8
|
+
import { ReviewableWorkflowGraphPlanningError } from './reviewable-workflow-graph-planning-error.js';
|
|
9
|
+
import { prepareWorkflowConnectionMaterials } from './prepare-workflow-connection-materials.js';
|
|
10
|
+
import { createPreparedNativeNodeMaterials } from './create-prepared-native-node-materials.js';
|
|
11
|
+
export async function planWorkflowConnections(input) {
|
|
12
|
+
assertSelectionInput(input.scenario, input.model);
|
|
13
|
+
const { materials, capabilities, nativeNodes, gaps, edges, contracts } = prepareWorkflowConnectionMaterials(input);
|
|
14
|
+
const context = { materials, capabilities, gaps };
|
|
15
|
+
const readyIds = materials.flatMap((material) => material.status === 'ready' ? [material.arguments.callId] : []);
|
|
16
|
+
const schema = createReviewableWorkflowGraphSchema([], readyIds);
|
|
17
|
+
const output = await input.model
|
|
18
|
+
.withStructuredOutput(schema, {
|
|
19
|
+
name: 'plan_workflow_connections',
|
|
20
|
+
method: 'functionCalling',
|
|
21
|
+
strict: true,
|
|
22
|
+
})
|
|
23
|
+
.invoke([
|
|
24
|
+
new SystemMessage(workflowConnectionsPrompt),
|
|
25
|
+
new HumanMessage(JSON.stringify({
|
|
26
|
+
scenario: input.scenario,
|
|
27
|
+
nodes: contracts,
|
|
28
|
+
...(nativeNodes.length
|
|
29
|
+
? {
|
|
30
|
+
nativeNodes: createPreparedNativeNodeMaterials(nativeNodes, capabilities),
|
|
31
|
+
}
|
|
32
|
+
: {}),
|
|
33
|
+
edges,
|
|
34
|
+
gaps,
|
|
35
|
+
readyCallIds: readyIds,
|
|
36
|
+
})),
|
|
37
|
+
]);
|
|
38
|
+
let proposal;
|
|
39
|
+
try {
|
|
40
|
+
proposal = completeReviewableWorkflowGraphProposal(parseStructuredOutput(schema, output, 'model workflow connections'), 0, readyIds.length);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (!(error instanceof Error))
|
|
44
|
+
throw error;
|
|
45
|
+
throw new ReviewableWorkflowGraphPlanningError({ stage: 'proposal-schema', output }, error);
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
return createReviewableWorkflowGraphPlan({
|
|
49
|
+
...context,
|
|
50
|
+
proposal: {
|
|
51
|
+
...proposal,
|
|
52
|
+
nativeNodes,
|
|
53
|
+
edges: [...edges, ...proposal.edges],
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
if (!(error instanceof Error))
|
|
59
|
+
throw error;
|
|
60
|
+
throw new ReviewableWorkflowGraphPlanningError({ stage: 'graph-validation', output }, error);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { SystemMessage, HumanMessage } from '@langchain/core/messages';
|
|
2
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
3
|
+
import { createWorkflowGraphPlan, createNativeOutputContracts, createWorkflowPlanningCapabilities, validateNativeApiResponseReferences, } from '@openapi-flow/core';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { createWorkflowGraphProposalSchema } from '../schemas/workflow-graph-proposal-schema.js';
|
|
6
|
+
import { WorkflowGraphPlanningError } from './workflow-graph-planning-error.js';
|
|
7
|
+
import { workflowGraphPrompt } from '../prompts/workflow-graph-prompt.js';
|
|
8
|
+
import { assertSelectionInput } from '../legacy/planning/select-operation.js';
|
|
9
|
+
import { parseStructuredOutput } from '../legacy/planning/parse-structured-output.js';
|
|
10
|
+
import { createPreparedNativeNodeMaterials } from './create-prepared-native-node-materials.js';
|
|
11
|
+
import { assembleNativeNodeMaterials } from './assemble-native-node-materials.js';
|
|
12
|
+
export async function planWorkflowGraph(input) {
|
|
13
|
+
assertSelectionInput(input.scenario, input.model);
|
|
14
|
+
const preparedNativeNodes = input.preparedNativeNodes ?? [];
|
|
15
|
+
const apiCallIds = input.materials.map((material) => material.arguments.callId);
|
|
16
|
+
const planningCapabilities = createWorkflowPlanningCapabilities({
|
|
17
|
+
capabilities: input.capabilities,
|
|
18
|
+
apiCallIds,
|
|
19
|
+
});
|
|
20
|
+
const schema = createWorkflowGraphProposalSchema(planningCapabilities);
|
|
21
|
+
validateNativeApiResponseReferences({
|
|
22
|
+
nativeNodes: preparedNativeNodes,
|
|
23
|
+
capabilities: input.capabilities,
|
|
24
|
+
apiCallIds,
|
|
25
|
+
});
|
|
26
|
+
const nativeOutputs = createNativeOutputContracts({
|
|
27
|
+
nativeNodes: preparedNativeNodes,
|
|
28
|
+
capabilities: input.capabilities,
|
|
29
|
+
});
|
|
30
|
+
const output = await input.model
|
|
31
|
+
.withStructuredOutput(schema, {
|
|
32
|
+
name: 'plan_workflow_graph',
|
|
33
|
+
method: 'functionCalling',
|
|
34
|
+
strict: true,
|
|
35
|
+
})
|
|
36
|
+
.invoke([
|
|
37
|
+
new SystemMessage(workflowGraphPrompt),
|
|
38
|
+
new HumanMessage(JSON.stringify({
|
|
39
|
+
scenario: input.scenario,
|
|
40
|
+
preparedNativeNodes: createPreparedNativeNodeMaterials(preparedNativeNodes, input.capabilities),
|
|
41
|
+
nativeOutputs,
|
|
42
|
+
// Request values are already generated. Avoid duplicating the resolved
|
|
43
|
+
// response schemas through both operation and pathItem in this stage.
|
|
44
|
+
materials: input.materials.map(({ operation, arguments: args }) => ({
|
|
45
|
+
operation: {
|
|
46
|
+
key: operation.key,
|
|
47
|
+
method: operation.method,
|
|
48
|
+
path: operation.path,
|
|
49
|
+
operation: {
|
|
50
|
+
summary: operation.operation.summary,
|
|
51
|
+
description: operation.operation.description,
|
|
52
|
+
tags: operation.operation.tags,
|
|
53
|
+
responses: operation.operation.responses,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
arguments: {
|
|
57
|
+
callId: args.callId,
|
|
58
|
+
values: args.values,
|
|
59
|
+
bindings: args.bindings,
|
|
60
|
+
},
|
|
61
|
+
inputPorts: ['main'],
|
|
62
|
+
outputPorts: ['main'],
|
|
63
|
+
})),
|
|
64
|
+
capabilities: planningCapabilities.map((capability) => ({
|
|
65
|
+
name: capability.name,
|
|
66
|
+
description: capability.description,
|
|
67
|
+
parametersSchema: z.toJSONSchema(capability.parametersSchema),
|
|
68
|
+
waitsForAllInputs: capability.waitsForAllInputs,
|
|
69
|
+
exclusiveOutputPorts: capability.exclusiveOutputPorts,
|
|
70
|
+
})),
|
|
71
|
+
})),
|
|
72
|
+
]);
|
|
73
|
+
let proposal;
|
|
74
|
+
try {
|
|
75
|
+
proposal = parseStructuredOutput(schema, output, 'model workflow graph');
|
|
76
|
+
if (!isDeepStrictEqual(proposal, output))
|
|
77
|
+
throw new Error('native model schema must validate without defaults, coercion or transformation');
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
if (!(error instanceof Error))
|
|
81
|
+
throw error;
|
|
82
|
+
throw new WorkflowGraphPlanningError({ stage: 'proposal-schema', output }, error);
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
return createWorkflowGraphPlan({
|
|
86
|
+
...input,
|
|
87
|
+
proposal: {
|
|
88
|
+
edges: proposal.edges,
|
|
89
|
+
gaps: proposal.gaps,
|
|
90
|
+
nativeNodes: assembleNativeNodeMaterials(preparedNativeNodes, proposal.additionalNativeNodes),
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
if (!(error instanceof Error))
|
|
96
|
+
throw error;
|
|
97
|
+
throw new WorkflowGraphPlanningError({ stage: 'graph-validation', output: proposal }, error);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { PlanWorkflowConnectionsInput, WorkflowConnectionMaterials } from '../types/workflow-orchestration.js';
|
|
2
|
+
/** Validate immutable supplied data before paying for any orchestration model call. */
|
|
3
|
+
export declare function prepareWorkflowConnectionMaterials(input: PlanWorkflowConnectionsInput): WorkflowConnectionMaterials;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { createReviewableNodeContracts, createNativeOutputContracts, validateWorkflowEdges, validateApiArguments, validateApiBindingAssignments, validateNativeApiResponseReferences, } from '@openapi-flow/core';
|
|
2
|
+
/** Validate immutable supplied data before paying for any orchestration model call. */
|
|
3
|
+
export function prepareWorkflowConnectionMaterials(input) {
|
|
4
|
+
const materials = input.materials ?? [];
|
|
5
|
+
const capabilities = input.capabilities ?? [];
|
|
6
|
+
const nativeNodes = input.nativeNodes ?? [];
|
|
7
|
+
const gaps = input.gaps ?? [];
|
|
8
|
+
const edges = input.edges ?? [];
|
|
9
|
+
validateNativeApiResponseReferences({
|
|
10
|
+
nativeNodes,
|
|
11
|
+
capabilities,
|
|
12
|
+
apiCallIds: materials.map((material) => material.status === 'ready' ? material.arguments.callId : material.callId),
|
|
13
|
+
});
|
|
14
|
+
const nativeOutputs = createNativeOutputContracts({
|
|
15
|
+
nativeNodes,
|
|
16
|
+
capabilities,
|
|
17
|
+
});
|
|
18
|
+
for (const material of materials) {
|
|
19
|
+
if (material.status !== 'ready')
|
|
20
|
+
continue;
|
|
21
|
+
const args = material.arguments;
|
|
22
|
+
const validation = validateApiArguments({
|
|
23
|
+
operation: material.operation,
|
|
24
|
+
...args,
|
|
25
|
+
});
|
|
26
|
+
if (args.unresolvedInputs.length || !validation.valid)
|
|
27
|
+
throw new Error(`materials[${args.callId}] has unresolved OAS request inputs`);
|
|
28
|
+
}
|
|
29
|
+
if (materials.length)
|
|
30
|
+
validateApiBindingAssignments({
|
|
31
|
+
nativeOutputs,
|
|
32
|
+
materials: materials.map((material) => ({
|
|
33
|
+
operation: material.operation,
|
|
34
|
+
callId: material.status === 'ready'
|
|
35
|
+
? material.arguments.callId
|
|
36
|
+
: material.callId,
|
|
37
|
+
requestMediaType: material.status === 'ready'
|
|
38
|
+
? material.arguments.requestMediaType
|
|
39
|
+
: material.requestMediaType,
|
|
40
|
+
})),
|
|
41
|
+
calls: materials.map((material) => ({
|
|
42
|
+
callId: material.status === 'ready'
|
|
43
|
+
? material.arguments.callId
|
|
44
|
+
: material.callId,
|
|
45
|
+
bindings: material.status === 'ready'
|
|
46
|
+
? material.arguments.bindings
|
|
47
|
+
: material.bindings,
|
|
48
|
+
})),
|
|
49
|
+
});
|
|
50
|
+
const plan = {
|
|
51
|
+
nativeNodes,
|
|
52
|
+
gaps,
|
|
53
|
+
edges,
|
|
54
|
+
starts: [],
|
|
55
|
+
blockedCalls: materials.flatMap((material) => material.status === 'blocked'
|
|
56
|
+
? [{ callId: material.callId, gapIds: material.gapIds }]
|
|
57
|
+
: []),
|
|
58
|
+
};
|
|
59
|
+
const contracts = createReviewableNodeContracts({
|
|
60
|
+
materials,
|
|
61
|
+
capabilities,
|
|
62
|
+
plan,
|
|
63
|
+
});
|
|
64
|
+
validateWorkflowEdges({ contracts, edges });
|
|
65
|
+
return { materials, capabilities, nativeNodes, gaps, edges, contracts };
|
|
66
|
+
}
|