@openapi-flow/langchain 0.3.18 → 0.4.1

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.
Files changed (137) hide show
  1. package/README.md +21 -0
  2. package/dist/arguments/analyze-api-input-answers.js +34 -6
  3. package/dist/arguments/api-argument-planning-error.d.ts +11 -0
  4. package/dist/arguments/api-argument-planning-error.js +32 -0
  5. package/dist/arguments/assert-api-argument-generation-source.d.ts +4 -0
  6. package/dist/arguments/assert-api-argument-generation-source.js +16 -0
  7. package/dist/arguments/attribute-api-literal-generation-sources.d.ts +4 -0
  8. package/dist/arguments/attribute-api-literal-generation-sources.js +16 -0
  9. package/dist/arguments/create-api-argument-rejection.d.ts +3 -0
  10. package/dist/arguments/create-api-argument-rejection.js +38 -0
  11. package/dist/arguments/fingerprint-api-argument-source.d.ts +4 -0
  12. package/dist/arguments/fingerprint-api-argument-source.js +13 -0
  13. package/dist/arguments/generate-api-arguments.js +108 -40
  14. package/dist/arguments/interpret-api-argument-enums.js +1 -0
  15. package/dist/arguments/resolve-api-literal-generation-output.d.ts +3 -0
  16. package/dist/arguments/resolve-api-literal-generation-output.js +14 -0
  17. package/dist/bindings/plan-api-bindings-by-call.js +12 -3
  18. package/dist/bindings/plan-api-call-bindings.js +30 -9
  19. package/dist/bindings/select-api-binding-sources.js +32 -8
  20. package/dist/model/invoke-structured-model.js +32 -6
  21. package/dist/prompts/api-arguments-prompt.d.ts +2 -1
  22. package/dist/prompts/api-arguments-prompt.js +11 -1
  23. package/dist/prompts/api-input-answers-prompt.d.ts +1 -1
  24. package/dist/prompts/api-input-answers-prompt.js +8 -7
  25. package/dist/prompts/api-material-repair-prompt.d.ts +1 -0
  26. package/dist/prompts/api-material-repair-prompt.js +1 -0
  27. package/dist/prompts/incremental-workflow-connections-prompt.d.ts +2 -2
  28. package/dist/prompts/incremental-workflow-connections-prompt.js +5 -4
  29. package/dist/prompts/native-instance-selection-review-prompt.d.ts +1 -0
  30. package/dist/prompts/native-instance-selection-review-prompt.js +9 -0
  31. package/dist/prompts/native-node-planning-prompt.d.ts +1 -1
  32. package/dist/prompts/native-node-planning-prompt.js +2 -0
  33. package/dist/prompts/workflow-connection-goal-recovery-prompt.d.ts +1 -0
  34. package/dist/prompts/workflow-connection-goal-recovery-prompt.js +8 -0
  35. package/dist/prompts/workflow-connection-goal-review-prompt.d.ts +1 -0
  36. package/dist/prompts/workflow-connection-goal-review-prompt.js +7 -0
  37. package/dist/prompts/workflow-connection-native-repair-prompt.d.ts +1 -0
  38. package/dist/prompts/workflow-connection-native-repair-prompt.js +1 -0
  39. package/dist/prompts/workflow-connection-outline-prompt.d.ts +1 -0
  40. package/dist/prompts/workflow-connection-outline-prompt.js +6 -0
  41. package/dist/prompts/workflow-connection-revision-prompt.d.ts +1 -0
  42. package/dist/prompts/workflow-connection-revision-prompt.js +10 -0
  43. package/dist/public-api.d.ts +28 -0
  44. package/dist/public-api.js +21 -0
  45. package/dist/schemas/api-argument-rejection-schema.d.ts +28 -0
  46. package/dist/schemas/api-argument-rejection-schema.js +20 -0
  47. package/dist/schemas/api-input-answer-source-schema.d.ts +5 -5
  48. package/dist/schemas/api-input-answer-source-schema.js +14 -6
  49. package/dist/schemas/api-literal-generation-output-schema.d.ts +35 -4
  50. package/dist/schemas/api-literal-generation-output-schema.js +49 -14
  51. package/dist/schemas/create-connection-material-repair-choice-schema.d.ts +4 -0
  52. package/dist/schemas/create-connection-material-repair-choice-schema.js +23 -0
  53. package/dist/schemas/model-signal-schema.d.ts +3 -0
  54. package/dist/schemas/model-signal-schema.js +3 -0
  55. package/dist/schemas/native-node-parameter-contract.js +2 -0
  56. package/dist/schemas/native-node-planning-contract.js +30 -23
  57. package/dist/schemas/native-planning-rejection-schema.d.ts +11 -0
  58. package/dist/schemas/native-planning-rejection-schema.js +8 -0
  59. package/dist/schemas/workflow-connection-goal-recovery-schema.d.ts +4 -0
  60. package/dist/schemas/workflow-connection-goal-recovery-schema.js +16 -0
  61. package/dist/schemas/workflow-connection-goal-rejection-schema.d.ts +76 -0
  62. package/dist/schemas/workflow-connection-goal-rejection-schema.js +2 -1
  63. package/dist/schemas/workflow-connection-goal-review-schema.d.ts +4 -0
  64. package/dist/schemas/workflow-connection-goal-review-schema.js +26 -0
  65. package/dist/schemas/workflow-connection-goals-schema.d.ts +4 -2
  66. package/dist/schemas/workflow-connection-outline-rejection-schema.d.ts +25 -0
  67. package/dist/schemas/workflow-connection-outline-rejection-schema.js +21 -0
  68. package/dist/schemas/workflow-connection-outline-schema.d.ts +3 -0
  69. package/dist/schemas/workflow-connection-outline-schema.js +25 -0
  70. package/dist/schemas/workflow-connection-revision-rejection-schema.d.ts +113 -0
  71. package/dist/schemas/workflow-connection-revision-rejection-schema.js +23 -0
  72. package/dist/schemas/workflow-connection-revision-schema.d.ts +4 -0
  73. package/dist/schemas/workflow-connection-revision-schema.js +23 -0
  74. package/dist/schemas/workflow-connection-step-schema.d.ts +5 -3
  75. package/dist/schemas/workflow-connection-step-schema.js +62 -31
  76. package/dist/types/api-call-binding-planning.d.ts +2 -1
  77. package/dist/types/api-input-answers.d.ts +3 -6
  78. package/dist/types/argument-generation.d.ts +37 -12
  79. package/dist/types/incremental-connection-planning.d.ts +10 -3
  80. package/dist/types/native-instance-selection-review.d.ts +12 -0
  81. package/dist/types/native-instance-selection-review.js +1 -0
  82. package/dist/types/native-node-planning.d.ts +10 -2
  83. package/dist/types/native-planning-rejection.d.ts +21 -0
  84. package/dist/types/native-planning-rejection.js +1 -0
  85. package/dist/types/workflow-connection-goal-recovery.d.ts +22 -0
  86. package/dist/types/workflow-connection-goal-recovery.js +1 -0
  87. package/dist/types/workflow-connection-goal-review.d.ts +13 -0
  88. package/dist/types/workflow-connection-goal-review.js +1 -0
  89. package/dist/types/workflow-connection-native-repair.d.ts +16 -0
  90. package/dist/types/workflow-connection-native-repair.js +1 -0
  91. package/dist/types/workflow-connection-outline.d.ts +22 -0
  92. package/dist/types/workflow-connection-outline.js +1 -0
  93. package/dist/types/workflow-connection-revision.d.ts +34 -0
  94. package/dist/types/workflow-connection-revision.js +1 -0
  95. package/dist/workflow/connections/create-workflow-connection-goal-rejection.js +8 -2
  96. package/dist/workflow/connections/create-workflow-connection-outline-rejection.d.ts +3 -0
  97. package/dist/workflow/connections/create-workflow-connection-outline-rejection.js +28 -0
  98. package/dist/workflow/connections/create-workflow-connection-revision-rejection.d.ts +3 -0
  99. package/dist/workflow/connections/create-workflow-connection-revision-rejection.js +32 -0
  100. package/dist/workflow/connections/describe-workflow-connection-outline-request.d.ts +3 -0
  101. package/dist/workflow/connections/describe-workflow-connection-outline-request.js +30 -0
  102. package/dist/workflow/connections/generate-workflow-connection-native-parameters.d.ts +4 -0
  103. package/dist/workflow/connections/generate-workflow-connection-native-parameters.js +80 -0
  104. package/dist/workflow/connections/invoke-connection-planning-model.js +3 -0
  105. package/dist/workflow/connections/plan-workflow-connection-goal-recovery.d.ts +3 -0
  106. package/dist/workflow/connections/plan-workflow-connection-goal-recovery.js +111 -0
  107. package/dist/workflow/connections/plan-workflow-connection-goals.js +35 -16
  108. package/dist/workflow/connections/plan-workflow-connection-native-dependencies.d.ts +4 -0
  109. package/dist/workflow/connections/plan-workflow-connection-native-dependencies.js +73 -0
  110. package/dist/workflow/connections/plan-workflow-connection-outline.d.ts +4 -0
  111. package/dist/workflow/connections/plan-workflow-connection-outline.js +93 -0
  112. package/dist/workflow/connections/plan-workflow-connection-revision.d.ts +3 -0
  113. package/dist/workflow/connections/plan-workflow-connection-revision.js +163 -0
  114. package/dist/workflow/connections/plan-workflow-connection-step.js +75 -29
  115. package/dist/workflow/connections/resolve-connection-selections.d.ts +6 -0
  116. package/dist/workflow/connections/resolve-connection-selections.js +28 -0
  117. package/dist/workflow/connections/review-workflow-connection-goals.d.ts +4 -0
  118. package/dist/workflow/connections/review-workflow-connection-goals.js +64 -0
  119. package/dist/workflow/connections/validate-workflow-goal-review-feedback.d.ts +4 -0
  120. package/dist/workflow/connections/validate-workflow-goal-review-feedback.js +22 -0
  121. package/dist/workflow/connections/workflow-connection-native-repair-error.d.ts +6 -0
  122. package/dist/workflow/connections/workflow-connection-native-repair-error.js +18 -0
  123. package/dist/workflow/connections/workflow-connection-planning-error.d.ts +1 -0
  124. package/dist/workflow/connections/workflow-connection-planning-error.js +2 -0
  125. package/dist/workflow/native-selection/create-native-planning-rejection.d.ts +4 -0
  126. package/dist/workflow/native-selection/create-native-planning-rejection.js +45 -0
  127. package/dist/workflow/native-selection/describe-native-planning-request.d.ts +5 -0
  128. package/dist/workflow/native-selection/describe-native-planning-request.js +26 -0
  129. package/dist/workflow/native-selection/native-instance-selection-review-error.d.ts +6 -0
  130. package/dist/workflow/native-selection/native-instance-selection-review-error.js +18 -0
  131. package/dist/workflow/native-selection/review-native-instance-selection.d.ts +4 -0
  132. package/dist/workflow/native-selection/review-native-instance-selection.js +97 -0
  133. package/dist/workflow/native-selection/validate-native-planning-rejections.d.ts +2 -0
  134. package/dist/workflow/native-selection/validate-native-planning-rejections.js +14 -0
  135. package/dist/workflow/resolve-native-node-planning-output.js +3 -1
  136. package/dist/workflow/select-native-node-instances.js +44 -17
  137. package/package.json +2 -2
package/README.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @openapi-flow/langchain
2
2
 
3
+ 0.4.0 adds independent selection/goal review, scoped connection
4
+ outlines and originating-stage recovery planners. Each call is independently
5
+ composable; loops, budget, checkpoints and user review stay in the host. Connection
6
+ steps select explicit start/finish intent, while code resolves a unique actual
7
+ port and validates the whole patch. No missing edge is treated as completion.
8
+ Custom model doubles must follow the supplied structured schema. Publication and
9
+ accepted limitations are tracked in the
10
+ [release record](https://github.com/carefreelife98/openapi-flow/blob/main/docs/releases/0.4.0.md).
11
+
12
+ 0.4.0 source adds `reviewNativeInstanceSelection` and explicit
13
+ `selectNativeNodeInstances({ feedback: { selection, review }, ...source })`.
14
+ Review makes at most one model call (zero with no reported gaps), uses exact supplied
15
+ gap IDs and producer/capability choices, and retains the original selection unchanged.
16
+ Only `needs-reselection` feedback permits a new explicitly requested selection;
17
+ unsupported behavior and business decisions cannot be bypassed this way. Ordinary
18
+ selection and configuration validation still applies to the new candidate. The host
19
+ owns budgets, checkpointing, user questions and whether to invoke either function;
20
+ there is no hidden loop, output repair or execution approval. Classification is model
21
+ evidence, not a proof of scenario correctness. See [native planning](../../docs/native-feature-planning.md).
22
+ This is not part of published 0.3.18.
23
+
3
24
  Version **0.3.18** distinguishes independent inputs, selected response sources,
4
25
  missing requested sources and partially missing sources in the model output.
5
26
  Code constructs only the fixed fields implied by the validated decision. Invalid
@@ -1,10 +1,12 @@
1
1
  import { HumanMessage, SystemMessage } from '@langchain/core/messages';
2
- import { captureApiInputAnswerContext, createApiInputAnswerExtractionRecord, resolveApiInputAnswerInterpretations, describeApiInputEnumAnswerChoices, describeApiInputSources, resolveApiInputSourceSelections, runOpenApiFlowOperation, } from '@openapi-flow/core';
2
+ import { isDeepStrictEqual } from 'node:util';
3
+ import { captureApiInputAnswerContext, createApiInputAnswerExtractionRecord, resolveApiInputAnswerInterpretations, describeApiInputEnumAnswerChoices, describeApiInputSources, describeApiInputMessageSources, hasApiInputAnswerSourceText, resolveApiInputSourceSelections, runOpenApiFlowOperation, } from '@openapi-flow/core';
3
4
  import { createApiInputAnswerSourceSchema, apiInputAnswerExplanationOptionsSchema, } from '../schemas/api-input-answer-source-schema.js';
4
5
  import { apiInputAnswersPrompt } from '../prompts/api-input-answers-prompt.js';
5
6
  import { serializeStructuredOutputSchema } from '../schemas/serialize-structured-output-schema.js';
6
7
  import { parseStructuredOutput } from '../legacy/planning/parse-structured-output.js';
7
8
  import { invokeStructuredModel } from '../model/invoke-structured-model.js';
9
+ import { modelSignalSchema } from '../schemas/model-signal-schema.js';
8
10
  export async function analyzeApiInputAnswers(input) {
9
11
  const { options, snapshot } = runOpenApiFlowOperation({
10
12
  owner: 'langchain',
@@ -12,12 +14,16 @@ export async function analyzeApiInputAnswers(input) {
12
14
  stage: 'input-validation',
13
15
  code: 'PLANNING_INPUT_INVALID',
14
16
  }, () => {
17
+ modelSignalSchema.parse(input.signal);
15
18
  const options = apiInputAnswerExplanationOptionsSchema.parse({
16
19
  includeExplanations: input.includeExplanations,
17
20
  selectOasEnums: input.selectOasEnums,
18
21
  });
19
22
  const snapshot = captureApiInputAnswerContext({
20
23
  reply: input.reply,
24
+ ...(input.sourceMessages === undefined
25
+ ? {}
26
+ : { sourceMessages: input.sourceMessages }),
21
27
  requirements: input.requirements,
22
28
  ...(input.purpose === undefined ? {} : { purpose: input.purpose }),
23
29
  ...(options.selectOasEnums === undefined
@@ -29,7 +35,7 @@ export async function analyzeApiInputAnswers(input) {
29
35
  throw new Error('analyzeApiInputAnswers.model must support structured output when supplied');
30
36
  return { options, snapshot };
31
37
  });
32
- if (!snapshot.requirements.length || snapshot.reply === '')
38
+ if (!snapshot.requirements.length || !hasApiInputAnswerSourceText(snapshot))
33
39
  return {
34
40
  status: snapshot.requirements.length
35
41
  ? 'empty-reply'
@@ -38,8 +44,21 @@ export async function analyzeApiInputAnswers(input) {
38
44
  sources: [],
39
45
  unansweredRequirementIds: snapshot.requirements.map((item) => item.id),
40
46
  };
41
- const catalog = describeApiInputSources(snapshot.reply);
42
- if (!catalog.json.length && !catalog.text.length)
47
+ const catalog = snapshot.sourceMessages === undefined
48
+ ? describeApiInputSources(snapshot.reply)
49
+ : undefined;
50
+ const messageSources = snapshot.sourceMessages === undefined
51
+ ? undefined
52
+ : describeApiInputMessageSources(snapshot.sourceMessages);
53
+ let hasSources;
54
+ if (messageSources !== undefined)
55
+ hasSources = messageSources.some(({ sources }) => sources.json.length > 0 || sources.text.length > 0);
56
+ else {
57
+ if (catalog === undefined)
58
+ throw new Error('API input answer sources are missing');
59
+ hasSources = catalog.json.length > 0 || catalog.text.length > 0;
60
+ }
61
+ if (!hasSources)
43
62
  return {
44
63
  status: 'extracted',
45
64
  answers: [],
@@ -55,7 +74,7 @@ export async function analyzeApiInputAnswers(input) {
55
74
  if (!input.model)
56
75
  throw new Error('analyzeApiInputAnswers.model is required for a nonempty reply with pending inputs');
57
76
  const enumChoices = describeApiInputEnumAnswerChoices(snapshot);
58
- const schema = createApiInputAnswerSourceSchema(snapshot.requirements, catalog, options.includeExplanations === true, enumChoices);
77
+ const schema = createApiInputAnswerSourceSchema(snapshot.requirements, catalog, options.includeExplanations === true, enumChoices, messageSources);
59
78
  return {
60
79
  model: input.model,
61
80
  schema,
@@ -70,6 +89,7 @@ export async function analyzeApiInputAnswers(input) {
70
89
  name: 'extract_api_input_answers',
71
90
  method: 'functionCalling',
72
91
  strict: true,
92
+ signal: input.signal,
73
93
  messages: [
74
94
  new SystemMessage(apiInputAnswersPrompt),
75
95
  new HumanMessage(JSON.stringify({
@@ -78,7 +98,9 @@ export async function analyzeApiInputAnswers(input) {
78
98
  ...(snapshot.purpose === undefined
79
99
  ? {}
80
100
  : { purpose: snapshot.purpose }),
81
- sources: catalog,
101
+ ...(catalog === undefined
102
+ ? { messageSources }
103
+ : { sources: catalog }),
82
104
  ...(enumChoices.length ? { enumChoices } : {}),
83
105
  })),
84
106
  ],
@@ -89,6 +111,9 @@ export async function analyzeApiInputAnswers(input) {
89
111
  stage: 'contract-validation',
90
112
  code: 'API_ARGUMENT_VALIDATION_FAILED',
91
113
  }, () => {
114
+ if (snapshot.sourceMessages !== undefined &&
115
+ !isDeepStrictEqual(snapshot.sourceMessages, input.sourceMessages))
116
+ throw new Error('sourceMessages changed during API input answer analysis');
92
117
  const parsed = parseStructuredOutput(schema, output, 'model clarification answer sources');
93
118
  const selections = resolveApiInputSourceSelections({
94
119
  ...snapshot,
@@ -115,6 +140,9 @@ export async function analyzeApiInputAnswers(input) {
115
140
  intent: {
116
141
  sourceFormat: intent.sourceFormat,
117
142
  sourceText: intent.sourceText,
143
+ ...(intent.messageId === undefined
144
+ ? {}
145
+ : { messageId: intent.messageId }),
118
146
  },
119
147
  reason: answer.reason,
120
148
  },
@@ -0,0 +1,11 @@
1
+ import { OpenApiFlowError } from '@openapi-flow/core';
2
+ import type { ApiArgumentPlanningFailure } from '../types/argument-generation.js';
3
+ /** Preserve the original typed error and cause; inherited toJSON exposes only its safe diagnostic. */
4
+ export declare class ApiArgumentPlanningError extends OpenApiFlowError {
5
+ readonly originalError: OpenApiFlowError;
6
+ readonly sourceFingerprint: string;
7
+ readonly output: unknown;
8
+ constructor(failure: ApiArgumentPlanningFailure);
9
+ }
10
+ /** Only this literal stage's output/contract rejection is eligible; transport/configuration/enum errors are not. */
11
+ export declare function rethrowApiArgumentPlanningError(error: unknown, sourceFingerprint: string, output: unknown): never;
@@ -0,0 +1,32 @@
1
+ import { OpenApiFlowError, isOpenApiFlowError } from '@openapi-flow/core';
2
+ /** Preserve the original typed error and cause; inherited toJSON exposes only its safe diagnostic. */
3
+ export class ApiArgumentPlanningError extends OpenApiFlowError {
4
+ originalError;
5
+ sourceFingerprint;
6
+ output;
7
+ constructor(failure) {
8
+ super({
9
+ message: failure.error.message,
10
+ diagnostic: failure.error.diagnostic,
11
+ cause: failure.error.cause,
12
+ });
13
+ this.originalError = failure.error;
14
+ this.sourceFingerprint = failure.sourceFingerprint;
15
+ this.output = failure.output;
16
+ }
17
+ }
18
+ /** Only this literal stage's output/contract rejection is eligible; transport/configuration/enum errors are not. */
19
+ export function rethrowApiArgumentPlanningError(error, sourceFingerprint, output) {
20
+ if (error instanceof ApiArgumentPlanningError)
21
+ throw error;
22
+ if (isOpenApiFlowError(error) &&
23
+ error.diagnostic.owner === 'langchain' &&
24
+ ((error.diagnostic.stage === 'model-output' &&
25
+ error.diagnostic.code === 'MODEL_OUTPUT_INVALID' &&
26
+ ['generateApiArguments', 'parseStructuredOutput'].includes(error.diagnostic.operation)) ||
27
+ (error.diagnostic.stage === 'contract-validation' &&
28
+ error.diagnostic.code === 'API_ARGUMENT_VALIDATION_FAILED' &&
29
+ error.diagnostic.operation === 'generateApiArguments')))
30
+ throw new ApiArgumentPlanningError({ error, sourceFingerprint, output });
31
+ throw error;
32
+ }
@@ -0,0 +1,4 @@
1
+ import type { ApiLiteralGenerationInput, WorkflowConnectionApiRepairDescription } from '@openapi-flow/core';
2
+ import type { GenerateApiArgumentsInput } from '../types/argument-generation.js';
3
+ /** Both asynchronous decisions must finish against the same explicit original source and repair request. */
4
+ export declare function assertApiArgumentGenerationSource(input: GenerateApiArgumentsInput, snapshot: ApiLiteralGenerationInput, materialRepair?: WorkflowConnectionApiRepairDescription): void;
@@ -0,0 +1,16 @@
1
+ import { isDeepStrictEqual } from 'node:util';
2
+ import { describeWorkflowConnectionApiRepair } from '@openapi-flow/core';
3
+ /** Both asynchronous decisions must finish against the same explicit original source and repair request. */
4
+ export function assertApiArgumentGenerationSource(input, snapshot, materialRepair) {
5
+ if (snapshot.literalSourceMessages !== undefined &&
6
+ !isDeepStrictEqual(snapshot.literalSourceMessages, input.literalSourceMessages))
7
+ throw new Error('literalSourceMessages changed during argument generation');
8
+ if (materialRepair !== undefined &&
9
+ (input.materialRepair === undefined ||
10
+ !isDeepStrictEqual(materialRepair, describeWorkflowConnectionApiRepair({
11
+ stage: 'api-arguments',
12
+ repair: input.materialRepair,
13
+ planning: input,
14
+ }))))
15
+ throw new Error('API material repair feedback changed during argument generation');
16
+ }
@@ -0,0 +1,4 @@
1
+ import type { ApiLiteralGenerationInput, ApiLiteralProposalSource } from '@openapi-flow/core';
2
+ import type { ApiLiteralGenerationSource } from '../types/argument-generation.js';
3
+ /** Single-source identity is code-owned; multi-source identity must be explicitly selected. */
4
+ export declare function attributeApiLiteralGenerationSources(input: ApiLiteralGenerationInput, sources: ApiLiteralGenerationSource[]): ApiLiteralProposalSource[];
@@ -0,0 +1,16 @@
1
+ /** Single-source identity is code-owned; multi-source identity must be explicitly selected. */
2
+ export function attributeApiLiteralGenerationSources(input, sources) {
3
+ const messages = input.literalSourceMessages;
4
+ if (messages === undefined)
5
+ return sources.map((source) => ({ ...source, messageId: 'scenario' }));
6
+ const first = messages[0];
7
+ if (!first)
8
+ throw new Error('literalSourceMessages must not be empty');
9
+ if (messages.length === 1)
10
+ return sources.map((source) => ({ ...source, messageId: first.id }));
11
+ return sources.map((source) => {
12
+ if (source.messageId === undefined)
13
+ throw new Error('Multiple literalSourceMessages require a source messageId');
14
+ return { ...source, messageId: source.messageId };
15
+ });
16
+ }
@@ -0,0 +1,3 @@
1
+ import type { CreateApiArgumentRejectionInput, ApiArgumentRejection } from '../types/argument-generation.js';
2
+ /** Return feedback only for the original unchanged literal source and lossless JSON output. */
3
+ export declare function createApiArgumentRejection(input: CreateApiArgumentRejectionInput): ApiArgumentRejection | undefined;
@@ -0,0 +1,38 @@
1
+ import { isDeepStrictEqual } from 'node:util';
2
+ import { describeApiLiteralGeneration, describeWorkflowConnectionApiRepair, } from '@openapi-flow/core';
3
+ import { apiLiteralGenerationOptionsSchema } from '../schemas/api-literal-generation-output-schema.js';
4
+ import { apiArgumentRejectionSchema } from '../schemas/api-argument-rejection-schema.js';
5
+ import { fingerprintApiArgumentSource } from './fingerprint-api-argument-source.js';
6
+ import { ApiArgumentPlanningError } from './api-argument-planning-error.js';
7
+ /** Return feedback only for the original unchanged literal source and lossless JSON output. */
8
+ export function createApiArgumentRejection(input) {
9
+ const error = input.error;
10
+ if (!(error instanceof ApiArgumentPlanningError))
11
+ return undefined;
12
+ const options = apiLiteralGenerationOptionsSchema.parse({
13
+ includeExplanations: input.includeExplanations,
14
+ selectOasEnums: input.selectOasEnums,
15
+ trackRequestedInputs: input.trackRequestedInputs,
16
+ });
17
+ const current = describeApiLiteralGeneration(input);
18
+ const materialRepair = input.materialRepair === undefined
19
+ ? undefined
20
+ : describeWorkflowConnectionApiRepair({
21
+ stage: 'api-arguments',
22
+ repair: input.materialRepair,
23
+ planning: input,
24
+ });
25
+ if (error.sourceFingerprint !==
26
+ fingerprintApiArgumentSource(current.input, options, materialRepair))
27
+ return undefined;
28
+ const result = apiArgumentRejectionSchema.safeParse({
29
+ sourceFingerprint: error.sourceFingerprint,
30
+ code: error.diagnostic.code,
31
+ stage: error.diagnostic.stage,
32
+ reason: error.message,
33
+ ...(error.output === undefined ? {} : { output: error.output }),
34
+ });
35
+ if (!result.success || !isDeepStrictEqual(result.data.output, error.output))
36
+ return undefined;
37
+ return structuredClone(result.data);
38
+ }
@@ -0,0 +1,4 @@
1
+ import type { ApiLiteralGenerationInput, WorkflowConnectionApiRepairDescription } from '@openapi-flow/core';
2
+ import type { ApiLiteralGenerationOptions } from '../types/argument-generation.js';
3
+ /** Only the validated Core snapshot and effective options participate, never a model/client or prior outputs. */
4
+ export declare function fingerprintApiArgumentSource(snapshot: ApiLiteralGenerationInput, options: ApiLiteralGenerationOptions, materialRepair?: WorkflowConnectionApiRepairDescription): string;
@@ -0,0 +1,13 @@
1
+ import { createHash } from 'node:crypto';
2
+ /** Only the validated Core snapshot and effective options participate, never a model/client or prior outputs. */
3
+ export function fingerprintApiArgumentSource(snapshot, options, materialRepair) {
4
+ return createHash('sha256')
5
+ .update(JSON.stringify({
6
+ snapshot,
7
+ includeExplanations: options.includeExplanations === true,
8
+ selectOasEnums: options.selectOasEnums === true,
9
+ trackRequestedInputs: options.trackRequestedInputs === true,
10
+ ...(materialRepair === undefined ? {} : { materialRepair }),
11
+ }))
12
+ .digest('hex');
13
+ }
@@ -1,20 +1,28 @@
1
1
  import { SystemMessage, HumanMessage } from '@langchain/core/messages';
2
- import { describeApiLiteralGeneration, createApiLiteralGenerationRecord, applyConfirmedApiInputs, createApiRequestSchema, inspectApiArgumentRequirements, resolveApiLiteralProposalSources, runOpenApiFlowOperation, runOpenApiFlowAsyncOperation, } from '@openapi-flow/core';
2
+ import { describeApiLiteralGeneration, createApiLiteralGenerationRecord, applyConfirmedApiInputs, createApiRequestSchema, inspectApiArgumentRequirements, resolveApiLiteralProposalSources, runOpenApiFlowOperation, runOpenApiFlowAsyncOperation, isOpenApiFlowError, describeWorkflowConnectionApiRepair, } from '@openapi-flow/core';
3
3
  import { apiLiteralGenerationOptionsSchema, createApiLiteralGenerationOutputSchema, } from '../schemas/api-literal-generation-output-schema.js';
4
- import { apiArgumentsPrompt } from '../prompts/api-arguments-prompt.js';
4
+ import { apiArgumentsPrompt, apiArgumentReplanningPrompt, } from '../prompts/api-arguments-prompt.js';
5
5
  import { parseStructuredOutput } from '../legacy/planning/parse-structured-output.js';
6
6
  import { interpretApiArgumentEnums } from './interpret-api-argument-enums.js';
7
7
  import { invokeStructuredModel } from '../model/invoke-structured-model.js';
8
+ import { resolveApiLiteralGenerationOutput } from './resolve-api-literal-generation-output.js';
9
+ import { apiArgumentRejectionsSchema } from '../schemas/api-argument-rejection-schema.js';
10
+ import { fingerprintApiArgumentSource } from './fingerprint-api-argument-source.js';
11
+ import { rethrowApiArgumentPlanningError } from './api-argument-planning-error.js';
12
+ import { modelSignalSchema } from '../schemas/model-signal-schema.js';
13
+ import { apiMaterialRepairPrompt } from '../prompts/api-material-repair-prompt.js';
14
+ import { attributeApiLiteralGenerationSources } from './attribute-api-literal-generation-sources.js';
15
+ import { assertApiArgumentGenerationSource } from './assert-api-argument-generation-source.js';
8
16
  export async function generateApiArguments(input) {
9
- const { options, snapshot, contract, context, trackInputs, schema } = runOpenApiFlowOperation({
17
+ const { options, snapshot, contract, context, trackInputs, schema, sourceFingerprint, rejectedProposals, materialRepair, } = runOpenApiFlowOperation({
10
18
  owner: 'langchain',
11
19
  operation: 'generateApiArguments',
12
20
  stage: 'input-validation',
13
21
  code: 'PLANNING_INPUT_INVALID',
14
22
  }, () => {
23
+ modelSignalSchema.parse(input.signal);
15
24
  if (input.model !== undefined &&
16
- (!input.model ||
17
- typeof input.model.withStructuredOutput !== 'function'))
25
+ (!input.model || typeof input.model.withStructuredOutput !== 'function'))
18
26
  throw new Error('generateApiArguments.model must support structured output when supplied');
19
27
  if (typeof input.callId !== 'string' || !input.callId.trim())
20
28
  throw new Error('callId must be non-empty');
@@ -24,61 +32,113 @@ export async function generateApiArguments(input) {
24
32
  trackRequestedInputs: input.trackRequestedInputs,
25
33
  });
26
34
  const { input: snapshot, contract, context, } = describeApiLiteralGeneration(input);
35
+ const materialRepair = input.materialRepair === undefined
36
+ ? undefined
37
+ : describeWorkflowConnectionApiRepair({
38
+ stage: 'api-arguments',
39
+ repair: input.materialRepair,
40
+ planning: input,
41
+ });
27
42
  const trackInputs = options.trackRequestedInputs === true ||
28
- options.selectOasEnums === true;
29
- const schema = createApiLiteralGenerationOutputSchema(contract, options.includeExplanations === true, trackInputs ? createApiRequestSchema(snapshot) : undefined);
43
+ options.selectOasEnums === true ||
44
+ snapshot.literalSourceMessages !== undefined ||
45
+ materialRepair !== undefined;
46
+ const schema = createApiLiteralGenerationOutputSchema(contract, options.includeExplanations === true, trackInputs ? createApiRequestSchema(snapshot) : undefined, snapshot.literalSourceMessages);
47
+ const sourceFingerprint = fingerprintApiArgumentSource(snapshot, options, materialRepair);
48
+ const rejectedProposals = input.rejectedProposals === undefined
49
+ ? undefined
50
+ : apiArgumentRejectionsSchema.parse(input.rejectedProposals);
51
+ if (rejectedProposals?.some((item) => item.sourceFingerprint !== sourceFingerprint))
52
+ throw new Error('API argument rejection feedback belongs to a different source');
53
+ if (!contract.hasLiteralInputs && rejectedProposals?.length)
54
+ throw new Error('API argument rejection feedback requires a literal model decision');
30
55
  if (contract.hasLiteralInputs && !input.model)
31
56
  throw new Error('generateApiArguments.model must support structured output when literal inputs remain');
32
- return { options, snapshot, contract, context, trackInputs, schema };
57
+ return {
58
+ options,
59
+ snapshot,
60
+ contract,
61
+ context,
62
+ trackInputs,
63
+ schema,
64
+ sourceFingerprint,
65
+ rejectedProposals,
66
+ materialRepair,
67
+ };
33
68
  });
34
- let proposed = {
35
- values: {},
36
- ...(trackInputs ? { requestedInputs: [], literalSources: [] } : {}),
37
- };
69
+ let proposed = trackInputs
70
+ ? { literalInputs: { state: 'not-provided' }, requestedInputs: [] }
71
+ : { values: {} };
38
72
  if (contract.hasLiteralInputs) {
39
73
  const model = input.model;
40
74
  if (!model || typeof model.withStructuredOutput !== 'function')
41
75
  throw new Error('generateApiArguments.model must support structured output when literal inputs remain');
42
- proposed = await invokeStructuredModel({
43
- model,
44
- schema,
45
- operation: 'generateApiArguments',
46
- name: 'generate_api_arguments',
47
- method: 'functionCalling',
48
- // Preserve the existing provider-default strictness of literal generation.
49
- strict: undefined,
50
- messages: [
51
- new SystemMessage(apiArgumentsPrompt),
52
- new HumanMessage(JSON.stringify({
53
- ...context,
54
- ...(options.selectOasEnums === true
55
- ? { selectOasEnums: true }
56
- : {}),
57
- })),
58
- ],
59
- });
76
+ try {
77
+ proposed = await invokeStructuredModel({
78
+ model,
79
+ schema,
80
+ operation: 'generateApiArguments',
81
+ name: 'generate_api_arguments',
82
+ method: 'functionCalling',
83
+ // Preserve the existing provider-default strictness of literal generation.
84
+ strict: undefined,
85
+ signal: input.signal,
86
+ messages: [
87
+ new SystemMessage([
88
+ apiArgumentsPrompt,
89
+ ...(rejectedProposals === undefined
90
+ ? []
91
+ : [apiArgumentReplanningPrompt]),
92
+ ...(materialRepair === undefined
93
+ ? []
94
+ : [apiMaterialRepairPrompt]),
95
+ ].join('\n')),
96
+ new HumanMessage(JSON.stringify({
97
+ ...context,
98
+ ...(rejectedProposals === undefined ? {} : { rejectedProposals }),
99
+ ...(materialRepair === undefined ? {} : { materialRepair }),
100
+ ...(options.selectOasEnums === true
101
+ ? { selectOasEnums: true }
102
+ : {}),
103
+ })),
104
+ ],
105
+ });
106
+ }
107
+ catch (error) {
108
+ const cause = isOpenApiFlowError(error) ? error.cause : undefined;
109
+ // The parser's documented raw output is retained as-is, never parsed/repaired from its message.
110
+ const output = typeof cause === 'object' &&
111
+ cause !== null &&
112
+ Object.getOwnPropertyDescriptor(cause, 'lc_error_code')?.value ===
113
+ 'OUTPUT_PARSING_FAILURE'
114
+ ? Object.getOwnPropertyDescriptor(cause, 'llmOutput')?.value
115
+ : undefined;
116
+ rethrowApiArgumentPlanningError(error, sourceFingerprint, output);
117
+ }
60
118
  }
119
+ let interpretingEnums = false;
61
120
  return runOpenApiFlowAsyncOperation({
62
121
  owner: 'langchain',
63
122
  operation: 'generateApiArguments',
64
123
  stage: 'contract-validation',
65
124
  code: 'API_ARGUMENT_VALIDATION_FAILED',
66
125
  }, async () => {
67
- const parsed = parseStructuredOutput(schema, proposed, `model arguments for ${snapshot.callId}`);
126
+ assertApiArgumentGenerationSource(input, snapshot, materialRepair);
127
+ const output = parseStructuredOutput(schema, proposed, `model arguments for ${snapshot.callId}`);
128
+ const parsed = resolveApiLiteralGenerationOutput(output);
68
129
  const values = applyConfirmedApiInputs({
69
130
  ...snapshot,
70
131
  values: parsed.values,
71
132
  });
72
- const requestedInputs = 'requestedInputs' in parsed ? parsed.requestedInputs : undefined;
73
- const literalSources = 'literalSources' in parsed
133
+ const requestedInputs = parsed.requestedInputs;
134
+ const literalSources = parsed.literalSources !== undefined
74
135
  ? resolveApiLiteralProposalSources({
75
136
  callId: snapshot.callId,
76
137
  values: parsed.values,
77
- sources: parsed.literalSources.map((source) => ({
78
- ...source,
79
- messageId: 'scenario',
80
- })),
81
- messages: [{ id: 'scenario', text: snapshot.scenario }],
138
+ sources: attributeApiLiteralGenerationSources(snapshot, parsed.literalSources),
139
+ messages: snapshot.literalSourceMessages === undefined
140
+ ? [{ id: 'scenario', text: snapshot.scenario }]
141
+ : snapshot.literalSourceMessages,
82
142
  })
83
143
  : undefined;
84
144
  const inspection = inspectApiArgumentRequirements({
@@ -100,7 +160,7 @@ export async function generateApiArguments(input) {
100
160
  ? {
101
161
  literalGeneration: createApiLiteralGenerationRecord({
102
162
  ...snapshot,
103
- ...('explanation' in parsed
163
+ ...(parsed.explanation !== undefined
104
164
  ? {
105
165
  proposal: { values: parsed.values },
106
166
  reason: parsed.explanation.reason,
@@ -113,15 +173,23 @@ export async function generateApiArguments(input) {
113
173
  if (options.selectOasEnums === true) {
114
174
  if (literalSources === undefined)
115
175
  throw new Error('generateApiArguments enum interpretation requires source-verified literalSources');
116
- return interpretApiArgumentEnums({
176
+ interpretingEnums = true;
177
+ const interpreted = await interpretApiArgumentEnums({
117
178
  ...snapshot,
118
179
  ...options,
119
180
  arguments: result,
120
181
  proposal: { values: parsed.values },
121
182
  literalSources,
122
183
  model: input.model,
184
+ signal: input.signal,
123
185
  });
186
+ assertApiArgumentGenerationSource(input, snapshot, materialRepair);
187
+ return interpreted;
124
188
  }
125
189
  return result;
190
+ }).catch((error) => {
191
+ if (interpretingEnums || !contract.hasLiteralInputs)
192
+ throw error;
193
+ rethrowApiArgumentPlanningError(error, sourceFingerprint, proposed);
126
194
  });
127
195
  }
@@ -13,6 +13,7 @@ export async function interpretApiArgumentEnums(input) {
13
13
  ...context,
14
14
  model: input.model,
15
15
  includeExplanations: input.includeExplanations,
16
+ signal: input.signal,
16
17
  });
17
18
  const resolved = resolveApiArgumentEnumInterpretation({
18
19
  ...material,
@@ -0,0 +1,3 @@
1
+ import type { ApiLiteralGenerationOutput, ResolvedApiLiteralGenerationOutput } from '../types/argument-generation.js';
2
+ /** Only an explicit absence decision produces empty values; supplied empty literals are never removed. */
3
+ export declare function resolveApiLiteralGenerationOutput(output: ApiLiteralGenerationOutput): ResolvedApiLiteralGenerationOutput;
@@ -0,0 +1,14 @@
1
+ /** Only an explicit absence decision produces empty values; supplied empty literals are never removed. */
2
+ export function resolveApiLiteralGenerationOutput(output) {
3
+ if ('literalInputs' in output) {
4
+ const literals = output.literalInputs;
5
+ return {
6
+ ...(literals.state === 'not-provided'
7
+ ? { values: {}, literalSources: [] }
8
+ : { values: literals.values, literalSources: literals.literalSources }),
9
+ requestedInputs: output.requestedInputs,
10
+ ...('explanation' in output ? { explanation: output.explanation } : {}),
11
+ };
12
+ }
13
+ return output;
14
+ }
@@ -1,4 +1,4 @@
1
- import { describeApiBindingPlanning, validateApiCallBindingPlan, assembleApiBindingPlan, runOpenApiFlowOperation, } from '@openapi-flow/core';
1
+ import { describeApiBindingPlanning, validateApiCallBindingPlan, assembleApiBindingPlan, runOpenApiFlowOperation, isOpenApiFlowError, } from '@openapi-flow/core';
2
2
  import { apiBindingCallExecutionSchema } from '../schemas/api-call-binding-plan-schema.js';
3
3
  import { selectApiBindingSources } from './select-api-binding-sources.js';
4
4
  import { planApiCallBindings } from './plan-api-call-bindings.js';
@@ -59,8 +59,17 @@ export async function planApiBindingsByCall(input) {
59
59
  results.set(result.callId, result);
60
60
  }
61
61
  catch (error) {
62
- if (!failed)
63
- failure = error;
62
+ if (!failed) {
63
+ // This aggregate API historically returns the caller's exact cancellation reason.
64
+ // A late structured result must not change that public cancellation contract.
65
+ failure =
66
+ input.signal?.aborted &&
67
+ isOpenApiFlowError(error) &&
68
+ error.diagnostic.code === 'MODEL_ABORTED' &&
69
+ error.cause === input.signal.reason
70
+ ? input.signal.reason
71
+ : error;
72
+ }
64
73
  failed = true;
65
74
  }
66
75
  }
@@ -1,12 +1,13 @@
1
1
  import { isDeepStrictEqual } from 'node:util';
2
2
  import { HumanMessage, SystemMessage } from '@langchain/core/messages';
3
- import { describeApiCallBindingPlanning, resolveApiBindingSourceSelection, resolveApiCallBindingPlan, listApiResponsePointers, runOpenApiFlowOperation, } from '@openapi-flow/core';
3
+ import { describeApiCallBindingPlanning, resolveApiBindingSourceSelection, resolveApiCallBindingPlan, listApiResponsePointers, runOpenApiFlowOperation, describeWorkflowConnectionApiRepair, } from '@openapi-flow/core';
4
4
  import { createApiCallBindingPlanSchema } from '../schemas/api-call-binding-plan-schema.js';
5
5
  import { apiCallBindingsPrompt } from '../prompts/api-call-bindings-prompt.js';
6
6
  import { invokeStructuredModel } from '../model/invoke-structured-model.js';
7
7
  import { parseStructuredOutput } from '../legacy/planning/parse-structured-output.js';
8
+ import { apiMaterialRepairPrompt } from '../prompts/api-material-repair-prompt.js';
8
9
  export async function planApiCallBindings(input) {
9
- const { description, sources } = runOpenApiFlowOperation({
10
+ const { description, sources, materialRepair } = runOpenApiFlowOperation({
10
11
  owner: 'langchain',
11
12
  operation: 'planApiCallBindings',
12
13
  stage: 'input-validation',
@@ -24,7 +25,14 @@ export async function planApiCallBindings(input) {
24
25
  });
25
26
  if (!isDeepStrictEqual(sources, expected))
26
27
  throw new Error('binding source selection changed or belongs to another call or source snapshot');
27
- return { description, sources };
28
+ const materialRepair = input.materialRepair === undefined
29
+ ? undefined
30
+ : describeWorkflowConnectionApiRepair({
31
+ stage: 'api-bindings',
32
+ repair: input.materialRepair,
33
+ planning: input,
34
+ });
35
+ return { description, sources, materialRepair };
28
36
  });
29
37
  input.signal?.throwIfAborted();
30
38
  if (!sources.sourceNodeIds.length)
@@ -48,7 +56,9 @@ export async function planApiCallBindings(input) {
48
56
  strict: true,
49
57
  signal: input.signal,
50
58
  messages: [
51
- new SystemMessage(apiCallBindingsPrompt),
59
+ new SystemMessage(materialRepair === undefined
60
+ ? apiCallBindingsPrompt
61
+ : [apiCallBindingsPrompt, apiMaterialRepairPrompt].join('\n')),
52
62
  new HumanMessage(JSON.stringify({
53
63
  scenario: description.input.scenario,
54
64
  target: description.target,
@@ -65,6 +75,7 @@ export async function planApiCallBindings(input) {
65
75
  responsePointerCatalog: listApiResponsePointers(material.operation),
66
76
  })),
67
77
  nativeSources: description.input.nativeOutputs?.filter((source) => selected.has(source.nodeId)),
78
+ ...(materialRepair === undefined ? {} : { materialRepair }),
68
79
  })),
69
80
  ],
70
81
  });
@@ -74,9 +85,19 @@ export async function planApiCallBindings(input) {
74
85
  operation: 'planApiCallBindings',
75
86
  stage: 'contract-validation',
76
87
  code: 'API_BINDING_VALIDATION_FAILED',
77
- }, () => resolveApiCallBindingPlan({
78
- ...description.input,
79
- sources,
80
- proposal: parseStructuredOutput(schema, output, 'API call bindings'),
81
- }));
88
+ }, () => {
89
+ if (materialRepair !== undefined &&
90
+ (input.materialRepair === undefined ||
91
+ !isDeepStrictEqual(materialRepair, describeWorkflowConnectionApiRepair({
92
+ stage: 'api-bindings',
93
+ repair: input.materialRepair,
94
+ planning: input,
95
+ }))))
96
+ throw new Error('API material repair feedback changed during binding planning');
97
+ return resolveApiCallBindingPlan({
98
+ ...description.input,
99
+ sources,
100
+ proposal: parseStructuredOutput(schema, output, 'API call bindings'),
101
+ });
102
+ });
82
103
  }