@codemation/core-nodes 0.4.3 → 1.0.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/CHANGELOG.md +44 -0
- package/dist/index.cjs +480 -410
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +220 -116
- package/dist/index.d.ts +220 -116
- package/dist/index.js +476 -403
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
- package/src/chatModels/OpenAIChatModelFactory.ts +17 -8
- package/src/chatModels/OpenAiStrictJsonSchemaFactory.ts +123 -0
- package/src/index.ts +1 -1
- package/src/nodes/AIAgentExecutionHelpersFactory.ts +45 -59
- package/src/nodes/AIAgentNode.ts +293 -288
- package/src/nodes/AgentMessageFactory.ts +57 -49
- package/src/nodes/AgentStructuredOutputRunner.ts +65 -71
- package/src/nodes/AgentToolExecutionCoordinator.ts +3 -14
- package/src/nodes/aiAgentSupport.types.ts +7 -2
- package/src/chatModels/OpenAIStructuredOutputMethodFactory.ts +0 -46
package/dist/index.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { AgentConfigInspector, AgentConnectionNodeCollector, AgentGuardrailDefaults, AgentMessageConfigNormalizer, CallableToolConfig, CodemationTelemetryAttributeNames, CodemationTelemetryMetricNames, ConnectionInvocationIdFactory, ConnectionNodeIdFactory, CoreTokens, DefinedNodeRegistry, GenAiTelemetryAttributeNames, ItemExprResolver, ItemsInputNormalizer, NodeBackedToolConfig, NodeOutputNormalizer, RetryPolicy, RunnableOutputBehaviorResolver, WorkflowBuilder, chatModel, emitPorts, getOriginIndexFromItem, inject, injectable, isPortsEmission, node } from "@codemation/core";
|
|
2
|
-
import {
|
|
3
|
-
import { AIMessage, HumanMessage, SystemMessage, ToolMessage } from "@langchain/core/messages";
|
|
4
|
-
import { isInteropZodSchema } from "@langchain/core/utils/types";
|
|
5
|
-
import { toJsonSchema } from "@langchain/core/utils/json_schema";
|
|
6
|
-
import { DynamicStructuredTool } from "@langchain/core/tools";
|
|
2
|
+
import { createOpenAI } from "@ai-sdk/openai";
|
|
7
3
|
import { CredentialResolverFactory } from "@codemation/core/bootstrap";
|
|
4
|
+
import { Output, generateText, jsonSchema } from "ai";
|
|
8
5
|
|
|
9
6
|
//#region \0@oxc-project+runtime@0.95.0/helpers/decorate.js
|
|
10
7
|
function __decorate(decorators, target, key, desc) {
|
|
@@ -19,166 +16,21 @@ function __decorate(decorators, target, key, desc) {
|
|
|
19
16
|
let OpenAIChatModelFactory = class OpenAIChatModelFactory$1 {
|
|
20
17
|
async create(args) {
|
|
21
18
|
const session = await args.ctx.getCredential(args.config.credentialSlotKey);
|
|
22
|
-
return new ChatOpenAI({
|
|
23
|
-
apiKey: session.apiKey,
|
|
24
|
-
model: args.config.model,
|
|
25
|
-
temperature: args.config.options?.temperature,
|
|
26
|
-
maxTokens: args.config.options?.maxTokens,
|
|
27
|
-
configuration: session.baseUrl ? { baseURL: session.baseUrl } : void 0
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
OpenAIChatModelFactory = __decorate([chatModel({ packageName: "@codemation/core-nodes" })], OpenAIChatModelFactory);
|
|
32
|
-
|
|
33
|
-
//#endregion
|
|
34
|
-
//#region src/chatModels/OpenAIStructuredOutputMethodFactory.ts
|
|
35
|
-
var _OpenAIStructuredOutputMethodFactory;
|
|
36
|
-
let OpenAIStructuredOutputMethodFactory = class OpenAIStructuredOutputMethodFactory$1 {
|
|
37
|
-
static {
|
|
38
|
-
_OpenAIStructuredOutputMethodFactory = this;
|
|
39
|
-
}
|
|
40
|
-
static isoDatePattern = /^\d{4}-\d{2}-\d{2}$/;
|
|
41
|
-
create(chatModelConfig) {
|
|
42
|
-
if (chatModelConfig.type !== OpenAIChatModelFactory) return;
|
|
43
|
-
const model = this.readModelName(chatModelConfig);
|
|
44
|
-
if (!model) return {
|
|
45
|
-
method: "functionCalling",
|
|
46
|
-
strict: true
|
|
47
|
-
};
|
|
48
|
-
return {
|
|
49
|
-
method: this.supportsJsonSchema(model) ? "jsonSchema" : "functionCalling",
|
|
50
|
-
strict: true
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
readModelName(chatModelConfig) {
|
|
54
|
-
const candidate = chatModelConfig;
|
|
55
|
-
return typeof candidate.model === "string" ? candidate.model : void 0;
|
|
56
|
-
}
|
|
57
|
-
supportsJsonSchema(model) {
|
|
58
|
-
if (model === "gpt-4o" || model === "gpt-4o-mini") return true;
|
|
59
|
-
return this.supportsSnapshotAtOrAfter(model, "gpt-4o-", "2024-08-06") || this.supportsSnapshotAtOrAfter(model, "gpt-4o-mini-", "2024-07-18");
|
|
60
|
-
}
|
|
61
|
-
supportsSnapshotAtOrAfter(model, prefix, minimumSnapshotDate) {
|
|
62
|
-
if (!model.startsWith(prefix)) return false;
|
|
63
|
-
const snapshotDate = model.slice(prefix.length);
|
|
64
|
-
return _OpenAIStructuredOutputMethodFactory.isoDatePattern.test(snapshotDate) && snapshotDate >= minimumSnapshotDate;
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
OpenAIStructuredOutputMethodFactory = _OpenAIStructuredOutputMethodFactory = __decorate([injectable()], OpenAIStructuredOutputMethodFactory);
|
|
68
|
-
|
|
69
|
-
//#endregion
|
|
70
|
-
//#region src/chatModels/openAiChatModelConfig.ts
|
|
71
|
-
var OpenAIChatModelConfig = class {
|
|
72
|
-
type = OpenAIChatModelFactory;
|
|
73
|
-
presentation;
|
|
74
|
-
provider = "openai";
|
|
75
|
-
modelName;
|
|
76
|
-
constructor(name, model, credentialSlotKey = "openai", presentationIn, options) {
|
|
77
|
-
this.name = name;
|
|
78
|
-
this.model = model;
|
|
79
|
-
this.credentialSlotKey = credentialSlotKey;
|
|
80
|
-
this.options = options;
|
|
81
|
-
this.modelName = model;
|
|
82
|
-
this.presentation = presentationIn ?? {
|
|
83
|
-
icon: "builtin:openai",
|
|
84
|
-
label: name
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
getCredentialRequirements() {
|
|
88
|
-
return [{
|
|
89
|
-
slotKey: this.credentialSlotKey,
|
|
90
|
-
label: "OpenAI API key",
|
|
91
|
-
acceptedTypes: ["openai.apiKey"]
|
|
92
|
-
}];
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
//#endregion
|
|
97
|
-
//#region src/chatModels/OpenAiChatModelPresetsFactory.ts
|
|
98
|
-
/**
|
|
99
|
-
* Default OpenAI chat model configs for scaffolds and demos (icon + label match {@link OpenAIChatModelConfig} defaults).
|
|
100
|
-
* Prefer importing {@link openAiChatModelPresets} from here or from the consumer template re-export
|
|
101
|
-
* instead of repeating {@link OpenAIChatModelConfig} construction in app workflows.
|
|
102
|
-
*/
|
|
103
|
-
var OpenAiChatModelPresets = class {
|
|
104
|
-
demoGpt4oMini = new OpenAIChatModelConfig("OpenAI", "gpt-4o-mini");
|
|
105
|
-
demoGpt41 = new OpenAIChatModelConfig("OpenAI", "gpt-4.1");
|
|
106
|
-
};
|
|
107
|
-
const openAiChatModelPresets = new OpenAiChatModelPresets();
|
|
108
|
-
|
|
109
|
-
//#endregion
|
|
110
|
-
//#region src/nodes/AgentMessageFactory.ts
|
|
111
|
-
var AgentMessageFactory = class {
|
|
112
|
-
static createPromptMessages(messages) {
|
|
113
|
-
return messages.map((message) => this.createPromptMessage(message));
|
|
114
|
-
}
|
|
115
|
-
static createSystemPrompt(systemMessage) {
|
|
116
|
-
return new SystemMessage(systemMessage);
|
|
117
|
-
}
|
|
118
|
-
static createUserPrompt(prompt) {
|
|
119
|
-
return new HumanMessage(prompt);
|
|
120
|
-
}
|
|
121
|
-
static createAssistantPrompt(prompt) {
|
|
122
|
-
return new AIMessage(prompt);
|
|
123
|
-
}
|
|
124
|
-
static createToolMessage(toolCallId, content) {
|
|
125
|
-
return new ToolMessage({
|
|
126
|
-
tool_call_id: toolCallId,
|
|
127
|
-
content
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
static extractContent(message) {
|
|
131
|
-
if (typeof message === "string") return message;
|
|
132
|
-
if (!this.isRecord(message)) return String(message);
|
|
133
|
-
const content = message.content;
|
|
134
|
-
if (typeof content === "string") return content;
|
|
135
|
-
if (Array.isArray(content)) return content.map((part) => {
|
|
136
|
-
if (typeof part === "string") return part;
|
|
137
|
-
if (this.isRecord(part) && typeof part.text === "string") return part.text;
|
|
138
|
-
return JSON.stringify(part);
|
|
139
|
-
}).join("\n");
|
|
140
|
-
return JSON.stringify(content);
|
|
141
|
-
}
|
|
142
|
-
static extractToolCalls(message) {
|
|
143
|
-
if (!this.isRecord(message)) return [];
|
|
144
|
-
const toolCalls = message.tool_calls;
|
|
145
|
-
if (!Array.isArray(toolCalls)) return [];
|
|
146
|
-
return toolCalls.filter((toolCall) => this.isRecord(toolCall) && typeof toolCall.name === "string").map((toolCall) => ({
|
|
147
|
-
id: typeof toolCall.id === "string" ? toolCall.id : void 0,
|
|
148
|
-
name: toolCall.name,
|
|
149
|
-
input: this.isRecord(toolCall) && "args" in toolCall ? toolCall.args : void 0
|
|
150
|
-
}));
|
|
151
|
-
}
|
|
152
|
-
static isRecord(value) {
|
|
153
|
-
return typeof value === "object" && value !== null;
|
|
154
|
-
}
|
|
155
|
-
static createPromptMessage(message) {
|
|
156
|
-
if (message.role === "system") return this.createSystemPrompt(message.content);
|
|
157
|
-
if (message.role === "assistant") return this.createAssistantPrompt(message.content);
|
|
158
|
-
return this.createUserPrompt(message.content);
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
//#endregion
|
|
163
|
-
//#region src/nodes/AgentOutputFactory.ts
|
|
164
|
-
var AgentOutputFactory = class {
|
|
165
|
-
static fromUnknown(value) {
|
|
166
|
-
return { main: [{ json: value }] };
|
|
167
|
-
}
|
|
168
|
-
static replaceJson(item, value) {
|
|
169
19
|
return {
|
|
170
|
-
|
|
171
|
-
|
|
20
|
+
languageModel: createOpenAI({
|
|
21
|
+
apiKey: session.apiKey,
|
|
22
|
+
baseURL: session.baseUrl
|
|
23
|
+
}).chat(args.config.model),
|
|
24
|
+
modelName: args.config.model,
|
|
25
|
+
provider: "openai",
|
|
26
|
+
defaultCallOptions: {
|
|
27
|
+
maxOutputTokens: args.config.options?.maxTokens,
|
|
28
|
+
temperature: args.config.options?.temperature
|
|
29
|
+
}
|
|
172
30
|
};
|
|
173
31
|
}
|
|
174
|
-
static fromAgentContent(content) {
|
|
175
|
-
try {
|
|
176
|
-
return JSON.parse(content);
|
|
177
|
-
} catch {
|
|
178
|
-
return { output: content };
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
32
|
};
|
|
33
|
+
OpenAIChatModelFactory = __decorate([chatModel({ packageName: "@codemation/core-nodes" })], OpenAIChatModelFactory);
|
|
182
34
|
|
|
183
35
|
//#endregion
|
|
184
36
|
//#region ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/core/core.js
|
|
@@ -2100,53 +1952,35 @@ let AIAgentExecutionHelpersFactory = class AIAgentExecutionHelpersFactory$1 {
|
|
|
2100
1952
|
createConnectionCredentialExecutionContextFactory(credentialSessions) {
|
|
2101
1953
|
return new ConnectionCredentialExecutionContextFactory(credentialSessions);
|
|
2102
1954
|
}
|
|
2103
|
-
createDynamicStructuredTool(entry, toolCredentialContext, item, itemIndex, items) {
|
|
2104
|
-
if (entry.runtime.inputSchema == null) throw new Error(`Cannot create LangChain tool "${entry.config.name}": missing inputSchema (broken tool runtime resolution).`);
|
|
2105
|
-
const schemaForOpenAi = this.createJsonSchemaRecord(entry.runtime.inputSchema, {
|
|
2106
|
-
schemaName: entry.config.name,
|
|
2107
|
-
requireObjectRoot: true
|
|
2108
|
-
});
|
|
2109
|
-
return new DynamicStructuredTool({
|
|
2110
|
-
name: entry.config.name,
|
|
2111
|
-
description: entry.config.description ?? entry.runtime.defaultDescription,
|
|
2112
|
-
schema: schemaForOpenAi,
|
|
2113
|
-
func: async (input) => {
|
|
2114
|
-
const result = await entry.runtime.execute({
|
|
2115
|
-
config: entry.config,
|
|
2116
|
-
input,
|
|
2117
|
-
ctx: toolCredentialContext,
|
|
2118
|
-
item,
|
|
2119
|
-
itemIndex,
|
|
2120
|
-
items
|
|
2121
|
-
});
|
|
2122
|
-
return JSON.stringify(result);
|
|
2123
|
-
}
|
|
2124
|
-
});
|
|
2125
|
-
}
|
|
2126
1955
|
/**
|
|
2127
|
-
* Produces a plain JSON Schema object
|
|
2128
|
-
* -
|
|
2129
|
-
*
|
|
2130
|
-
*
|
|
2131
|
-
*
|
|
2132
|
-
* -
|
|
1956
|
+
* Produces a plain JSON Schema object (`draft-07`) from a Zod schema, as needed by
|
|
1957
|
+
* OpenAI tool-parameter schemas and the structured-output repair prompt.
|
|
1958
|
+
* - Prefers the schema's **instance** `toJSONSchema(...)` method so we stay inside the Zod
|
|
1959
|
+
* instance that created the schema (works across consumer/framework tsx namespaces — see
|
|
1960
|
+
* {@link ZodInstanceToJsonSchema}). Falls back to the framework-imported module function.
|
|
1961
|
+
* - Strips root `$schema` (OpenAI ignores it).
|
|
1962
|
+
* - Sanitizes `required` for cfworker json-schema compatibility (must be a string array or absent).
|
|
2133
1963
|
*/
|
|
2134
1964
|
createJsonSchemaRecord(inputSchema, options) {
|
|
2135
|
-
const
|
|
2136
|
-
|
|
2137
|
-
if (
|
|
2138
|
-
else {
|
|
2139
|
-
converted = toJsonSchema(inputSchema);
|
|
2140
|
-
if (isInteropZodSchema(converted)) converted = toJSONSchema(inputSchema, draft07Params);
|
|
2141
|
-
}
|
|
2142
|
-
const { $schema: _draftSchemaOmitted,...rest } = converted;
|
|
2143
|
-
if (options.requireObjectRoot && rest.type !== "object") throw new Error(`Cannot create LangChain tool "${options.schemaName}": tool input schema must be a JSON Schema object type (got type=${String(rest.type)}).`);
|
|
2144
|
-
if (options.requireObjectRoot && rest.properties !== void 0 && (typeof rest.properties !== "object" || Array.isArray(rest.properties))) throw new Error(`Cannot create LangChain tool "${options.schemaName}": tool input schema "properties" must be an object (got ${JSON.stringify(rest.properties)}).`);
|
|
1965
|
+
const { $schema: _draftSchemaOmitted,...rest } = this.convertZodSchemaToJsonSchema(inputSchema, { target: "draft-07" });
|
|
1966
|
+
if (options.requireObjectRoot && rest.type !== "object") throw new Error(`Cannot create tool "${options.schemaName}": tool input schema must be a JSON Schema object type (got type=${String(rest.type)}).`);
|
|
1967
|
+
if (options.requireObjectRoot && rest.properties !== void 0 && (typeof rest.properties !== "object" || Array.isArray(rest.properties))) throw new Error(`Cannot create tool "${options.schemaName}": tool input schema "properties" must be an object (got ${JSON.stringify(rest.properties)}).`);
|
|
2145
1968
|
if (options.requireObjectRoot && rest.properties === void 0) rest.properties = {};
|
|
2146
1969
|
this.sanitizeJsonSchemaRequiredKeywordsForCfworker(rest);
|
|
2147
1970
|
return rest;
|
|
2148
1971
|
}
|
|
2149
1972
|
/**
|
|
1973
|
+
* Runs Zod's `toJSONSchema` via the schema's own instance method when available, so consumer
|
|
1974
|
+
* schemas loaded under a different tsx namespace still convert correctly. If the caller handed us
|
|
1975
|
+
* a payload that lacks that method (e.g. a plain JSON Schema record or a Zod instance whose
|
|
1976
|
+
* prototype was stripped), we fall back to the framework-bundled module function.
|
|
1977
|
+
*/
|
|
1978
|
+
convertZodSchemaToJsonSchema(inputSchema, params) {
|
|
1979
|
+
const candidate = inputSchema.toJSONSchema;
|
|
1980
|
+
if (typeof candidate === "function") return candidate.call(inputSchema, params);
|
|
1981
|
+
return toJSONSchema(inputSchema, params);
|
|
1982
|
+
}
|
|
1983
|
+
/**
|
|
2150
1984
|
* `@cfworker/json-schema` iterates `schema.required` with `for...of`; it must be a string array or absent.
|
|
2151
1985
|
*/
|
|
2152
1986
|
sanitizeJsonSchemaRequiredKeywordsForCfworker(node$1) {
|
|
@@ -2193,6 +2027,214 @@ function __decorateParam(paramIndex, decorator) {
|
|
|
2193
2027
|
};
|
|
2194
2028
|
}
|
|
2195
2029
|
|
|
2030
|
+
//#endregion
|
|
2031
|
+
//#region src/chatModels/OpenAiStrictJsonSchemaFactory.ts
|
|
2032
|
+
var _ref$5;
|
|
2033
|
+
let OpenAiStrictJsonSchemaFactory = class OpenAiStrictJsonSchemaFactory$1 {
|
|
2034
|
+
constructor(executionHelpers) {
|
|
2035
|
+
this.executionHelpers = executionHelpers;
|
|
2036
|
+
}
|
|
2037
|
+
createStructuredOutputRecord(schema, options) {
|
|
2038
|
+
const record = this.executionHelpers.createJsonSchemaRecord(schema, {
|
|
2039
|
+
schemaName: options.schemaName,
|
|
2040
|
+
requireObjectRoot: false
|
|
2041
|
+
});
|
|
2042
|
+
this.strictifyRecursive(record);
|
|
2043
|
+
if (options.title !== void 0) record.title = options.title;
|
|
2044
|
+
return record;
|
|
2045
|
+
}
|
|
2046
|
+
strictifyRecursive(node$1) {
|
|
2047
|
+
if (!node$1 || typeof node$1 !== "object" || Array.isArray(node$1)) return;
|
|
2048
|
+
const o = node$1;
|
|
2049
|
+
this.stripOpenAiRejectedKeywords(o);
|
|
2050
|
+
if (this.isObjectNode(o)) {
|
|
2051
|
+
const props = this.readPropertiesObject(o);
|
|
2052
|
+
o.properties = props;
|
|
2053
|
+
o.additionalProperties = false;
|
|
2054
|
+
o.required = Object.keys(props);
|
|
2055
|
+
for (const value of Object.values(props)) this.strictifyRecursive(value);
|
|
2056
|
+
}
|
|
2057
|
+
this.recurseIntoComposites(o);
|
|
2058
|
+
}
|
|
2059
|
+
stripOpenAiRejectedKeywords(o) {
|
|
2060
|
+
delete o["$schema"];
|
|
2061
|
+
delete o["unevaluatedProperties"];
|
|
2062
|
+
delete o["default"];
|
|
2063
|
+
}
|
|
2064
|
+
isObjectNode(o) {
|
|
2065
|
+
const typeIsObject = o.type === "object" || Array.isArray(o.type) && o.type.includes("object");
|
|
2066
|
+
const hasObjectProperties = o.properties !== void 0 && typeof o.properties === "object" && !Array.isArray(o.properties);
|
|
2067
|
+
return typeIsObject || hasObjectProperties;
|
|
2068
|
+
}
|
|
2069
|
+
readPropertiesObject(o) {
|
|
2070
|
+
if (o.properties && typeof o.properties === "object" && !Array.isArray(o.properties)) return o.properties;
|
|
2071
|
+
return {};
|
|
2072
|
+
}
|
|
2073
|
+
recurseIntoComposites(o) {
|
|
2074
|
+
for (const key of [
|
|
2075
|
+
"allOf",
|
|
2076
|
+
"anyOf",
|
|
2077
|
+
"oneOf",
|
|
2078
|
+
"prefixItems"
|
|
2079
|
+
]) {
|
|
2080
|
+
const branch = o[key];
|
|
2081
|
+
if (Array.isArray(branch)) for (const sub of branch) this.strictifyRecursive(sub);
|
|
2082
|
+
}
|
|
2083
|
+
if (o.not) this.strictifyRecursive(o.not);
|
|
2084
|
+
if (o.items) if (Array.isArray(o.items)) for (const sub of o.items) this.strictifyRecursive(sub);
|
|
2085
|
+
else this.strictifyRecursive(o.items);
|
|
2086
|
+
for (const key of [
|
|
2087
|
+
"if",
|
|
2088
|
+
"then",
|
|
2089
|
+
"else"
|
|
2090
|
+
]) if (o[key]) this.strictifyRecursive(o[key]);
|
|
2091
|
+
for (const key of ["$defs", "definitions"]) {
|
|
2092
|
+
const defs = o[key];
|
|
2093
|
+
if (defs && typeof defs === "object" && !Array.isArray(defs)) for (const sub of Object.values(defs)) this.strictifyRecursive(sub);
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
};
|
|
2097
|
+
OpenAiStrictJsonSchemaFactory = __decorate([
|
|
2098
|
+
injectable(),
|
|
2099
|
+
__decorateParam(0, inject(AIAgentExecutionHelpersFactory)),
|
|
2100
|
+
__decorateMetadata("design:paramtypes", [typeof (_ref$5 = typeof AIAgentExecutionHelpersFactory !== "undefined" && AIAgentExecutionHelpersFactory) === "function" ? _ref$5 : Object])
|
|
2101
|
+
], OpenAiStrictJsonSchemaFactory);
|
|
2102
|
+
|
|
2103
|
+
//#endregion
|
|
2104
|
+
//#region src/chatModels/openAiChatModelConfig.ts
|
|
2105
|
+
var OpenAIChatModelConfig = class {
|
|
2106
|
+
type = OpenAIChatModelFactory;
|
|
2107
|
+
presentation;
|
|
2108
|
+
provider = "openai";
|
|
2109
|
+
modelName;
|
|
2110
|
+
constructor(name, model, credentialSlotKey = "openai", presentationIn, options) {
|
|
2111
|
+
this.name = name;
|
|
2112
|
+
this.model = model;
|
|
2113
|
+
this.credentialSlotKey = credentialSlotKey;
|
|
2114
|
+
this.options = options;
|
|
2115
|
+
this.modelName = model;
|
|
2116
|
+
this.presentation = presentationIn ?? {
|
|
2117
|
+
icon: "builtin:openai",
|
|
2118
|
+
label: name
|
|
2119
|
+
};
|
|
2120
|
+
}
|
|
2121
|
+
getCredentialRequirements() {
|
|
2122
|
+
return [{
|
|
2123
|
+
slotKey: this.credentialSlotKey,
|
|
2124
|
+
label: "OpenAI API key",
|
|
2125
|
+
acceptedTypes: ["openai.apiKey"]
|
|
2126
|
+
}];
|
|
2127
|
+
}
|
|
2128
|
+
};
|
|
2129
|
+
|
|
2130
|
+
//#endregion
|
|
2131
|
+
//#region src/chatModels/OpenAiChatModelPresetsFactory.ts
|
|
2132
|
+
/**
|
|
2133
|
+
* Default OpenAI chat model configs for scaffolds and demos (icon + label match {@link OpenAIChatModelConfig} defaults).
|
|
2134
|
+
* Prefer importing {@link openAiChatModelPresets} from here or from the consumer template re-export
|
|
2135
|
+
* instead of repeating {@link OpenAIChatModelConfig} construction in app workflows.
|
|
2136
|
+
*/
|
|
2137
|
+
var OpenAiChatModelPresets = class {
|
|
2138
|
+
demoGpt4oMini = new OpenAIChatModelConfig("OpenAI", "gpt-4o-mini");
|
|
2139
|
+
demoGpt41 = new OpenAIChatModelConfig("OpenAI", "gpt-4.1");
|
|
2140
|
+
};
|
|
2141
|
+
const openAiChatModelPresets = new OpenAiChatModelPresets();
|
|
2142
|
+
|
|
2143
|
+
//#endregion
|
|
2144
|
+
//#region src/nodes/AgentMessageFactory.ts
|
|
2145
|
+
/**
|
|
2146
|
+
* AI-SDK-shaped message construction for the AIAgent stack. Emits plain `ModelMessage[]`
|
|
2147
|
+
* ( `{ role: 'system' | 'user' | 'assistant' | 'tool', content: ... }` ) as consumed by
|
|
2148
|
+
* `generateText({ messages })` from the `ai` package.
|
|
2149
|
+
*/
|
|
2150
|
+
var AgentMessageFactory = class AgentMessageFactory {
|
|
2151
|
+
static createPromptMessages(messages) {
|
|
2152
|
+
return messages.map((message) => this.createPromptMessage(message));
|
|
2153
|
+
}
|
|
2154
|
+
/**
|
|
2155
|
+
* Builds the assistant message that contains optional text plus one or more tool-call parts,
|
|
2156
|
+
* matching the shape AI SDK emits between steps.
|
|
2157
|
+
*/
|
|
2158
|
+
static createAssistantWithToolCalls(text, toolCalls) {
|
|
2159
|
+
const content = [];
|
|
2160
|
+
if (text && text.length > 0) content.push({
|
|
2161
|
+
type: "text",
|
|
2162
|
+
text
|
|
2163
|
+
});
|
|
2164
|
+
for (const toolCall of toolCalls) content.push({
|
|
2165
|
+
type: "tool-call",
|
|
2166
|
+
toolCallId: toolCall.id ?? toolCall.name,
|
|
2167
|
+
toolName: toolCall.name,
|
|
2168
|
+
input: toolCall.input ?? {}
|
|
2169
|
+
});
|
|
2170
|
+
return {
|
|
2171
|
+
role: "assistant",
|
|
2172
|
+
content
|
|
2173
|
+
};
|
|
2174
|
+
}
|
|
2175
|
+
/**
|
|
2176
|
+
* Builds the `{ role: "tool", content: [{ type: "tool-result", ... }, ...] }` message returned
|
|
2177
|
+
* to the model after each tool round.
|
|
2178
|
+
*/
|
|
2179
|
+
static createToolResultsMessage(executedToolCalls) {
|
|
2180
|
+
return {
|
|
2181
|
+
role: "tool",
|
|
2182
|
+
content: executedToolCalls.map((executed) => ({
|
|
2183
|
+
type: "tool-result",
|
|
2184
|
+
toolCallId: executed.toolCallId,
|
|
2185
|
+
toolName: executed.toolName,
|
|
2186
|
+
output: {
|
|
2187
|
+
type: "json",
|
|
2188
|
+
value: AgentMessageFactory.toToolResultJson(executed.result)
|
|
2189
|
+
}
|
|
2190
|
+
}))
|
|
2191
|
+
};
|
|
2192
|
+
}
|
|
2193
|
+
static toToolResultJson(value) {
|
|
2194
|
+
if (value === void 0) return null;
|
|
2195
|
+
try {
|
|
2196
|
+
return JSON.parse(JSON.stringify(value));
|
|
2197
|
+
} catch {
|
|
2198
|
+
return String(value);
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
static createPromptMessage(message) {
|
|
2202
|
+
if (message.role === "system") return {
|
|
2203
|
+
role: "system",
|
|
2204
|
+
content: message.content
|
|
2205
|
+
};
|
|
2206
|
+
if (message.role === "assistant") return {
|
|
2207
|
+
role: "assistant",
|
|
2208
|
+
content: message.content
|
|
2209
|
+
};
|
|
2210
|
+
return {
|
|
2211
|
+
role: "user",
|
|
2212
|
+
content: message.content
|
|
2213
|
+
};
|
|
2214
|
+
}
|
|
2215
|
+
};
|
|
2216
|
+
|
|
2217
|
+
//#endregion
|
|
2218
|
+
//#region src/nodes/AgentOutputFactory.ts
|
|
2219
|
+
var AgentOutputFactory = class {
|
|
2220
|
+
static fromUnknown(value) {
|
|
2221
|
+
return { main: [{ json: value }] };
|
|
2222
|
+
}
|
|
2223
|
+
static replaceJson(item, value) {
|
|
2224
|
+
return {
|
|
2225
|
+
...item,
|
|
2226
|
+
json: value
|
|
2227
|
+
};
|
|
2228
|
+
}
|
|
2229
|
+
static fromAgentContent(content) {
|
|
2230
|
+
try {
|
|
2231
|
+
return JSON.parse(content);
|
|
2232
|
+
} catch {
|
|
2233
|
+
return { output: content };
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
};
|
|
2237
|
+
|
|
2196
2238
|
//#endregion
|
|
2197
2239
|
//#region src/nodes/AgentStructuredOutputRepairPromptFactory.ts
|
|
2198
2240
|
var _ref$4, _AgentStructuredOutputRepairPromptFactory;
|
|
@@ -2571,31 +2613,26 @@ let AgentStructuredOutputRunner = class AgentStructuredOutputRunner$1 {
|
|
|
2571
2613
|
_AgentStructuredOutputRunner = this;
|
|
2572
2614
|
}
|
|
2573
2615
|
static repairAttemptCount = 2;
|
|
2574
|
-
|
|
2616
|
+
static structuredOutputSchemaName = "agent_output";
|
|
2617
|
+
constructor(repairPromptFactory, openAiStrictJsonSchemaFactory) {
|
|
2575
2618
|
this.repairPromptFactory = repairPromptFactory;
|
|
2576
|
-
this.
|
|
2619
|
+
this.openAiStrictJsonSchemaFactory = openAiStrictJsonSchemaFactory;
|
|
2577
2620
|
}
|
|
2578
2621
|
async resolve(args) {
|
|
2579
2622
|
let lastFailure;
|
|
2580
|
-
if (args.
|
|
2581
|
-
const directResult = this.tryParseAndValidate(
|
|
2582
|
-
if (directResult.ok) return directResult.value;
|
|
2583
|
-
lastFailure = directResult;
|
|
2584
|
-
} else if (!this.supportsNativeStructuredOutput(args.model)) {
|
|
2585
|
-
const rawResponse = await args.invokeTextModel(args.conversation);
|
|
2586
|
-
const directResult = this.tryParseAndValidate(AgentMessageFactory.extractContent(rawResponse), args.schema);
|
|
2623
|
+
if (args.rawFinalText !== void 0) {
|
|
2624
|
+
const directResult = this.tryParseAndValidate(args.rawFinalText, args.schema);
|
|
2587
2625
|
if (directResult.ok) return directResult.value;
|
|
2588
2626
|
lastFailure = directResult;
|
|
2589
2627
|
}
|
|
2590
2628
|
try {
|
|
2591
|
-
const
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
}
|
|
2629
|
+
const structuredOptions = this.resolveStructuredOutputOptions(args.chatModelConfig);
|
|
2630
|
+
const schemaForModel = this.resolveOutputSchemaForModel(args.schema, structuredOptions);
|
|
2631
|
+
const nativeResult = this.tryValidateStructuredValue(await args.invokeStructuredModel(schemaForModel, args.conversation, structuredOptions), args.schema);
|
|
2632
|
+
if (nativeResult.ok) return nativeResult.value;
|
|
2633
|
+
lastFailure = nativeResult;
|
|
2597
2634
|
} catch (error) {
|
|
2598
|
-
lastFailure = {
|
|
2635
|
+
lastFailure = lastFailure ?? {
|
|
2599
2636
|
ok: false,
|
|
2600
2637
|
invalidContent: "",
|
|
2601
2638
|
validationError: `Native structured output failed: ${this.summarizeError(error)}`
|
|
@@ -2619,22 +2656,26 @@ let AgentStructuredOutputRunner = class AgentStructuredOutputRunner$1 {
|
|
|
2619
2656
|
validationError: failure.validationError
|
|
2620
2657
|
}))];
|
|
2621
2658
|
const repairResponse = await args.invokeTextModel(repairMessages);
|
|
2622
|
-
const repairResult = this.tryParseAndValidate(
|
|
2659
|
+
const repairResult = this.tryParseAndValidate(repairResponse.text, args.schema);
|
|
2623
2660
|
if (repairResult.ok) return repairResult.value;
|
|
2624
2661
|
failure = repairResult;
|
|
2625
2662
|
}
|
|
2626
2663
|
throw new Error(`Structured output required for AIAgent "${args.agentName}" (${args.nodeId}) but validation still failed after ${_AgentStructuredOutputRunner.repairAttemptCount} repair attempts: ${failure.validationError}`);
|
|
2627
2664
|
}
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
return
|
|
2665
|
+
/**
|
|
2666
|
+
* Chooses strict mode for OpenAI chat-model configs, off otherwise. Extendable in future for
|
|
2667
|
+
* other providers that adopt the same "supply a JSON Schema record directly" contract.
|
|
2668
|
+
*/
|
|
2669
|
+
resolveStructuredOutputOptions(chatModelConfig) {
|
|
2670
|
+
if (chatModelConfig.type !== OpenAIChatModelFactory) return;
|
|
2671
|
+
return {
|
|
2672
|
+
strict: true,
|
|
2673
|
+
schemaName: _AgentStructuredOutputRunner.structuredOutputSchemaName
|
|
2674
|
+
};
|
|
2635
2675
|
}
|
|
2636
|
-
|
|
2637
|
-
|
|
2676
|
+
resolveOutputSchemaForModel(schema, options) {
|
|
2677
|
+
if (!options?.strict) return schema;
|
|
2678
|
+
return this.openAiStrictJsonSchemaFactory.createStructuredOutputRecord(schema, { schemaName: options.schemaName ?? _AgentStructuredOutputRunner.structuredOutputSchemaName });
|
|
2638
2679
|
}
|
|
2639
2680
|
tryParseAndValidate(content, schema) {
|
|
2640
2681
|
try {
|
|
@@ -2668,7 +2709,7 @@ let AgentStructuredOutputRunner = class AgentStructuredOutputRunner$1 {
|
|
|
2668
2709
|
}
|
|
2669
2710
|
toJson(value) {
|
|
2670
2711
|
try {
|
|
2671
|
-
return JSON.stringify(value);
|
|
2712
|
+
return JSON.stringify(value) ?? String(value);
|
|
2672
2713
|
} catch (error) {
|
|
2673
2714
|
return `<<unserializable: ${this.summarizeError(error)}>>`;
|
|
2674
2715
|
}
|
|
@@ -2677,8 +2718,8 @@ let AgentStructuredOutputRunner = class AgentStructuredOutputRunner$1 {
|
|
|
2677
2718
|
AgentStructuredOutputRunner = _AgentStructuredOutputRunner = __decorate([
|
|
2678
2719
|
injectable(),
|
|
2679
2720
|
__decorateParam(0, inject(AgentStructuredOutputRepairPromptFactory)),
|
|
2680
|
-
__decorateParam(1, inject(
|
|
2681
|
-
__decorateMetadata("design:paramtypes", [typeof (_ref$3 = typeof AgentStructuredOutputRepairPromptFactory !== "undefined" && AgentStructuredOutputRepairPromptFactory) === "function" ? _ref$3 : Object, typeof (_ref2$3 = typeof
|
|
2721
|
+
__decorateParam(1, inject(OpenAiStrictJsonSchemaFactory)),
|
|
2722
|
+
__decorateMetadata("design:paramtypes", [typeof (_ref$3 = typeof AgentStructuredOutputRepairPromptFactory !== "undefined" && AgentStructuredOutputRepairPromptFactory) === "function" ? _ref$3 : Object, typeof (_ref2$3 = typeof OpenAiStrictJsonSchemaFactory !== "undefined" && OpenAiStrictJsonSchemaFactory) === "function" ? _ref2$3 : Object])
|
|
2682
2723
|
], AgentStructuredOutputRunner);
|
|
2683
2724
|
|
|
2684
2725
|
//#endregion
|
|
@@ -2832,8 +2873,8 @@ let AgentToolExecutionCoordinator = class AgentToolExecutionCoordinator$1 {
|
|
|
2832
2873
|
inputsByPort: toolCallInputsByPort
|
|
2833
2874
|
});
|
|
2834
2875
|
try {
|
|
2835
|
-
const
|
|
2836
|
-
const
|
|
2876
|
+
const result = await plannedToolCall.binding.execute(plannedToolCall.toolCall.input ?? {});
|
|
2877
|
+
const serialized = typeof result === "string" ? result : JSON.stringify(result);
|
|
2837
2878
|
const finishedAt = /* @__PURE__ */ new Date();
|
|
2838
2879
|
await ctx.nodeState?.markCompleted({
|
|
2839
2880
|
nodeId: plannedToolCall.nodeId,
|
|
@@ -2877,7 +2918,7 @@ let AgentToolExecutionCoordinator = class AgentToolExecutionCoordinator$1 {
|
|
|
2877
2918
|
const classification = this.errorClassifier.classify({
|
|
2878
2919
|
error,
|
|
2879
2920
|
toolName: plannedToolCall.binding.config.name,
|
|
2880
|
-
schema: plannedToolCall.binding.
|
|
2921
|
+
schema: plannedToolCall.binding.inputSchema
|
|
2881
2922
|
});
|
|
2882
2923
|
if (classification.kind !== "repairable_validation_error") {
|
|
2883
2924
|
const effectiveError = classification.effectiveError;
|
|
@@ -3040,14 +3081,6 @@ let AgentToolExecutionCoordinator = class AgentToolExecutionCoordinator$1 {
|
|
|
3040
3081
|
if (!firstIssue) return `Your previous tool call for "${toolName}" was invalid and did not match the expected schema.`;
|
|
3041
3082
|
return `Your previous tool call for "${toolName}" was invalid because field "${firstIssue.path.length > 0 ? firstIssue.path.join(".") : "<root>"}" failed validation: ${firstIssue.message}`;
|
|
3042
3083
|
}
|
|
3043
|
-
parseToolOutput(serialized) {
|
|
3044
|
-
if (typeof serialized !== "string") return serialized;
|
|
3045
|
-
try {
|
|
3046
|
-
return JSON.parse(serialized);
|
|
3047
|
-
} catch {
|
|
3048
|
-
return serialized;
|
|
3049
|
-
}
|
|
3050
|
-
}
|
|
3051
3084
|
toJsonValue(value) {
|
|
3052
3085
|
if (value === void 0) return;
|
|
3053
3086
|
return JSON.parse(JSON.stringify(value));
|
|
@@ -3167,14 +3200,8 @@ var _ref, _ref2, _ref3, _ref4;
|
|
|
3167
3200
|
let AIAgentNode = class AIAgentNode$1 {
|
|
3168
3201
|
kind = "node";
|
|
3169
3202
|
outputPorts = ["main"];
|
|
3170
|
-
/**
|
|
3171
|
-
* Engine validates {@link RunnableNodeConfig.inputSchema} (Zod) on {@code item.json} before enqueue, then resolves
|
|
3172
|
-
* per-item **`itemExpr`** leaves on config before {@link #execute}. Prefer modeling prompts as
|
|
3173
|
-
* {@code { messages: [{ role, content }, ...] }} (on input or config) so persisted inputs are visible in the UI.
|
|
3174
|
-
*/
|
|
3175
3203
|
inputSchema = unknown();
|
|
3176
3204
|
connectionCredentialExecutionContextFactory;
|
|
3177
|
-
/** One resolved model/tools bundle per activation context (same ctx across items in a batch). */
|
|
3178
3205
|
preparedByExecutionContext = /* @__PURE__ */ new WeakMap();
|
|
3179
3206
|
constructor(nodeResolver, credentialSessions, nodeBackedToolRuntime, executionHelpers, structuredOutputRunner, toolExecutionCoordinator) {
|
|
3180
3207
|
this.nodeResolver = nodeResolver;
|
|
@@ -3205,9 +3232,6 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3205
3232
|
throw error;
|
|
3206
3233
|
}
|
|
3207
3234
|
}
|
|
3208
|
-
/**
|
|
3209
|
-
* Resolves the chat model and tools once per activation, then reuses for every item in the batch.
|
|
3210
|
-
*/
|
|
3211
3235
|
async prepareExecution(ctx) {
|
|
3212
3236
|
const chatModelFactory = this.nodeResolver.resolve(ctx.config.chatModel.type);
|
|
3213
3237
|
const languageModelCredentialContext = this.connectionCredentialExecutionContextFactory.forConnectionNode(ctx, {
|
|
@@ -3225,9 +3249,6 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3225
3249
|
languageModelConnectionNodeId: ConnectionNodeIdFactory.languageModelConnectionNodeId(ctx.nodeId)
|
|
3226
3250
|
};
|
|
3227
3251
|
}
|
|
3228
|
-
/**
|
|
3229
|
-
* One item: build prompts, optionally bind tools, run the multi-turn loop, map the final model message to workflow JSON.
|
|
3230
|
-
*/
|
|
3231
3252
|
async runAgentForItem(prepared, item, itemIndex, items) {
|
|
3232
3253
|
const { ctx } = prepared;
|
|
3233
3254
|
const itemInputsByPort = AgentItemPortMap.fromItem(item);
|
|
@@ -3241,8 +3262,8 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3241
3262
|
conversation,
|
|
3242
3263
|
agentName: this.getAgentDisplayName(ctx),
|
|
3243
3264
|
nodeId: ctx.nodeId,
|
|
3244
|
-
invokeTextModel: async (messages) => await this.
|
|
3245
|
-
invokeStructuredModel: async (
|
|
3265
|
+
invokeTextModel: async (messages) => await this.invokeTextTurn(prepared, itemInputsByPort, messages, []),
|
|
3266
|
+
invokeStructuredModel: async (schema, messages, structuredOptions) => await this.invokeStructuredTurn(prepared, itemInputsByPort, schema, messages, structuredOptions)
|
|
3246
3267
|
});
|
|
3247
3268
|
await ctx.telemetry.recordMetric({
|
|
3248
3269
|
name: CodemationTelemetryMetricNames.agentTurns,
|
|
@@ -3254,13 +3275,11 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3254
3275
|
});
|
|
3255
3276
|
return this.buildOutputItem(item, structuredOutput);
|
|
3256
3277
|
}
|
|
3257
|
-
const modelWithTools = this.bindToolsToModel(prepared.model, itemScopedTools);
|
|
3258
3278
|
const loopResult = await this.runTurnLoopUntilFinalAnswer({
|
|
3259
3279
|
prepared,
|
|
3260
3280
|
itemInputsByPort,
|
|
3261
3281
|
itemScopedTools,
|
|
3262
|
-
conversation
|
|
3263
|
-
modelWithTools
|
|
3282
|
+
conversation
|
|
3264
3283
|
});
|
|
3265
3284
|
await ctx.telemetry.recordMetric({
|
|
3266
3285
|
name: CodemationTelemetryMetricNames.agentTurns,
|
|
@@ -3270,30 +3289,34 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3270
3289
|
name: CodemationTelemetryMetricNames.agentToolCalls,
|
|
3271
3290
|
value: loopResult.toolCallCount
|
|
3272
3291
|
});
|
|
3273
|
-
const outputJson = await this.resolveFinalOutputJson(prepared, itemInputsByPort, conversation, loopResult.
|
|
3292
|
+
const outputJson = await this.resolveFinalOutputJson(prepared, itemInputsByPort, conversation, loopResult.finalText, itemScopedTools.length > 0);
|
|
3274
3293
|
return this.buildOutputItem(item, outputJson);
|
|
3275
3294
|
}
|
|
3276
3295
|
/**
|
|
3277
|
-
*
|
|
3296
|
+
* Multi-turn loop:
|
|
3297
|
+
* - Each turn is a single `generateText` call with tools exposed but **not auto-executed**
|
|
3298
|
+
* (we control tool dispatch so that {@link AgentToolExecutionCoordinator} drives repair /
|
|
3299
|
+
* connection-invocation recording / transient-error handling exactly like before).
|
|
3300
|
+
* - When the model returns no tool calls the loop ends with the model's text as the final answer.
|
|
3301
|
+
* - Respects `guardrails.maxTurns` and `guardrails.onTurnLimitReached`.
|
|
3278
3302
|
*/
|
|
3279
3303
|
async runTurnLoopUntilFinalAnswer(args) {
|
|
3280
|
-
const { prepared, itemInputsByPort, itemScopedTools, conversation
|
|
3281
|
-
const { ctx, guardrails
|
|
3282
|
-
let
|
|
3304
|
+
const { prepared, itemInputsByPort, itemScopedTools, conversation } = args;
|
|
3305
|
+
const { ctx, guardrails } = prepared;
|
|
3306
|
+
let finalText = "";
|
|
3283
3307
|
let toolCallCount = 0;
|
|
3284
3308
|
let turnCount = 0;
|
|
3285
3309
|
const repairAttemptsByToolName = /* @__PURE__ */ new Map();
|
|
3286
3310
|
for (let turn = 1; turn <= guardrails.maxTurns; turn++) {
|
|
3287
3311
|
turnCount = turn;
|
|
3288
|
-
const
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
if (toolCalls.length === 0) break;
|
|
3312
|
+
const result = await this.invokeTextTurn(prepared, itemInputsByPort, conversation, itemScopedTools);
|
|
3313
|
+
finalText = result.text;
|
|
3314
|
+
if (result.toolCalls.length === 0) break;
|
|
3292
3315
|
if (this.cannotExecuteAnotherToolRound(turn, guardrails)) {
|
|
3293
3316
|
this.finishOrThrowWhenTurnCapHitWithToolCalls(ctx, guardrails);
|
|
3294
3317
|
break;
|
|
3295
3318
|
}
|
|
3296
|
-
const plannedToolCalls = this.planToolCalls(itemScopedTools, toolCalls, ctx.nodeId);
|
|
3319
|
+
const plannedToolCalls = this.planToolCalls(itemScopedTools, result.toolCalls, ctx.nodeId);
|
|
3297
3320
|
toolCallCount += plannedToolCalls.length;
|
|
3298
3321
|
await this.markQueuedTools(plannedToolCalls, ctx);
|
|
3299
3322
|
const executedToolCalls = await this.toolExecutionCoordinator.execute({
|
|
@@ -3302,11 +3325,10 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3302
3325
|
agentName: this.getAgentDisplayName(ctx),
|
|
3303
3326
|
repairAttemptsByToolName
|
|
3304
3327
|
});
|
|
3305
|
-
this.appendAssistantAndToolMessages(conversation,
|
|
3328
|
+
this.appendAssistantAndToolMessages(conversation, result.assistantMessage, result.text, result.toolCalls, executedToolCalls);
|
|
3306
3329
|
}
|
|
3307
|
-
if (!finalResponse) throw new Error(`AIAgent "${ctx.config.name ?? ctx.nodeId}" did not produce a model response.`);
|
|
3308
3330
|
return {
|
|
3309
|
-
|
|
3331
|
+
finalText,
|
|
3310
3332
|
turnCount,
|
|
3311
3333
|
toolCallCount
|
|
3312
3334
|
};
|
|
@@ -3318,30 +3340,30 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3318
3340
|
if (guardrails.onTurnLimitReached === "respondWithLastMessage") return;
|
|
3319
3341
|
throw new Error(`AIAgent "${ctx.config.name ?? ctx.nodeId}" reached maxTurns=${guardrails.maxTurns} before producing a final response.`);
|
|
3320
3342
|
}
|
|
3321
|
-
appendAssistantAndToolMessages(conversation, assistantMessage, executedToolCalls) {
|
|
3322
|
-
conversation.push(assistantMessage
|
|
3343
|
+
appendAssistantAndToolMessages(conversation, assistantMessage, text, toolCalls, executedToolCalls) {
|
|
3344
|
+
conversation.push(assistantMessage ?? AgentMessageFactory.createAssistantWithToolCalls(text, toolCalls), AgentMessageFactory.createToolResultsMessage(executedToolCalls));
|
|
3323
3345
|
}
|
|
3324
|
-
async resolveFinalOutputJson(prepared, itemInputsByPort, conversation,
|
|
3325
|
-
if (!prepared.ctx.config.outputSchema) return AgentOutputFactory.fromAgentContent(
|
|
3346
|
+
async resolveFinalOutputJson(prepared, itemInputsByPort, conversation, finalText, wasToolEnabledRun) {
|
|
3347
|
+
if (!prepared.ctx.config.outputSchema) return AgentOutputFactory.fromAgentContent(finalText);
|
|
3348
|
+
const conversationWithFinal = wasToolEnabledRun ? [...conversation, {
|
|
3349
|
+
role: "assistant",
|
|
3350
|
+
content: finalText
|
|
3351
|
+
}] : conversation;
|
|
3326
3352
|
return await this.structuredOutputRunner.resolve({
|
|
3327
3353
|
model: prepared.model,
|
|
3328
3354
|
chatModelConfig: prepared.ctx.config.chatModel,
|
|
3329
3355
|
schema: prepared.ctx.config.outputSchema,
|
|
3330
|
-
conversation:
|
|
3331
|
-
|
|
3356
|
+
conversation: conversationWithFinal,
|
|
3357
|
+
rawFinalText: finalText,
|
|
3332
3358
|
agentName: this.getAgentDisplayName(prepared.ctx),
|
|
3333
3359
|
nodeId: prepared.ctx.nodeId,
|
|
3334
|
-
invokeTextModel: async (messages) => await this.
|
|
3335
|
-
invokeStructuredModel: async (
|
|
3360
|
+
invokeTextModel: async (messages) => await this.invokeTextTurn(prepared, itemInputsByPort, messages, []),
|
|
3361
|
+
invokeStructuredModel: async (schema, messages, structuredOptions) => await this.invokeStructuredTurn(prepared, itemInputsByPort, schema, messages, structuredOptions)
|
|
3336
3362
|
});
|
|
3337
3363
|
}
|
|
3338
3364
|
buildOutputItem(item, outputJson) {
|
|
3339
3365
|
return AgentOutputFactory.replaceJson(item, outputJson);
|
|
3340
3366
|
}
|
|
3341
|
-
bindToolsToModel(model, itemScopedTools) {
|
|
3342
|
-
if (itemScopedTools.length === 0 || !model.bindTools) return model;
|
|
3343
|
-
return model.bindTools(itemScopedTools.map((entry) => entry.langChainTool));
|
|
3344
|
-
}
|
|
3345
3367
|
resolveTools(toolConfigs) {
|
|
3346
3368
|
const resolvedTools = toolConfigs.map((config$1) => ({
|
|
3347
3369
|
config: config$1,
|
|
@@ -3360,38 +3382,93 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3360
3382
|
connectionNodeId: ConnectionNodeIdFactory.toolConnectionNodeId(ctx.nodeId, entry.config.name),
|
|
3361
3383
|
getCredentialRequirements: () => entry.config.getCredentialRequirements?.() ?? []
|
|
3362
3384
|
});
|
|
3363
|
-
const langChainTool = this.executionHelpers.createDynamicStructuredTool(entry, toolCredentialContext, item, itemIndex, items);
|
|
3364
3385
|
return {
|
|
3365
3386
|
config: entry.config,
|
|
3366
|
-
|
|
3387
|
+
inputSchema: entry.runtime.inputSchema,
|
|
3388
|
+
execute: async (input) => {
|
|
3389
|
+
const validated = entry.runtime.inputSchema.parse(input);
|
|
3390
|
+
return await entry.runtime.execute({
|
|
3391
|
+
config: entry.config,
|
|
3392
|
+
input: validated,
|
|
3393
|
+
ctx: toolCredentialContext,
|
|
3394
|
+
item,
|
|
3395
|
+
itemIndex,
|
|
3396
|
+
items
|
|
3397
|
+
});
|
|
3398
|
+
}
|
|
3367
3399
|
};
|
|
3368
3400
|
});
|
|
3369
3401
|
}
|
|
3370
|
-
|
|
3402
|
+
/**
|
|
3403
|
+
* Builds an AI SDK {@link ToolSet} where every tool ships a pre-converted JSON Schema (via
|
|
3404
|
+
* {@link jsonSchema}) — not the raw Zod schema — and carries **no** `execute`. Two reasons:
|
|
3405
|
+
*
|
|
3406
|
+
* 1. Codemation owns tool dispatch + the per-tool repair loop (see {@link AgentToolExecutionCoordinator}),
|
|
3407
|
+
* so the AI SDK must surface tool calls back to us instead of auto-running them.
|
|
3408
|
+
* 2. The AI SDK's `asSchema` helper discriminates between Zod v3 / Zod v4 / Standard Schema via
|
|
3409
|
+
* runtime feature-detection (`~standard`, `_zod`, etc.). Handing it a pre-built
|
|
3410
|
+
* {@link jsonSchema} record — which is tagged with `Symbol.for('vercel.ai.schema')` — skips all
|
|
3411
|
+
* of that detection and guarantees the provider receives a draft-07 JSON Schema with
|
|
3412
|
+
* `additionalProperties: false` at every object depth (see {@link OpenAiStrictJsonSchemaFactory}
|
|
3413
|
+
* for the same logic applied to structured-output schemas). Codemation still runs its own Zod
|
|
3414
|
+
* validation on tool inputs before execute — the schema handed to the model is advisory.
|
|
3415
|
+
*/
|
|
3416
|
+
buildToolSet(itemScopedTools) {
|
|
3417
|
+
if (itemScopedTools.length === 0) return void 0;
|
|
3418
|
+
const toolSet = {};
|
|
3419
|
+
for (const entry of itemScopedTools) {
|
|
3420
|
+
const schemaRecord = this.executionHelpers.createJsonSchemaRecord(entry.inputSchema, {
|
|
3421
|
+
schemaName: entry.config.name,
|
|
3422
|
+
requireObjectRoot: true
|
|
3423
|
+
});
|
|
3424
|
+
toolSet[entry.config.name] = {
|
|
3425
|
+
description: entry.config.description,
|
|
3426
|
+
inputSchema: jsonSchema(schemaRecord)
|
|
3427
|
+
};
|
|
3428
|
+
}
|
|
3429
|
+
return toolSet;
|
|
3430
|
+
}
|
|
3431
|
+
/**
|
|
3432
|
+
* One `generateText` turn (no auto tool execution) with Codemation-owned child-span telemetry
|
|
3433
|
+
* and connection-invocation state recording.
|
|
3434
|
+
*/
|
|
3435
|
+
async invokeTextTurn(prepared, itemInputsByPort, messages, itemScopedTools) {
|
|
3371
3436
|
const invocationId = ConnectionInvocationIdFactory.create();
|
|
3372
3437
|
const startedAt = /* @__PURE__ */ new Date();
|
|
3373
3438
|
const summarizedInput = this.summarizeLlmMessages(messages);
|
|
3439
|
+
const { ctx, model, languageModelConnectionNodeId, guardrails } = prepared;
|
|
3374
3440
|
const span = this.createModelInvocationSpan(ctx, invocationId, startedAt);
|
|
3375
3441
|
await ctx.nodeState?.markQueued({
|
|
3376
|
-
nodeId,
|
|
3442
|
+
nodeId: languageModelConnectionNodeId,
|
|
3377
3443
|
activationId: ctx.activationId,
|
|
3378
|
-
inputsByPort
|
|
3444
|
+
inputsByPort: itemInputsByPort
|
|
3379
3445
|
});
|
|
3380
3446
|
await ctx.nodeState?.markRunning({
|
|
3381
|
-
nodeId,
|
|
3447
|
+
nodeId: languageModelConnectionNodeId,
|
|
3382
3448
|
activationId: ctx.activationId,
|
|
3383
|
-
inputsByPort
|
|
3449
|
+
inputsByPort: itemInputsByPort
|
|
3384
3450
|
});
|
|
3385
3451
|
try {
|
|
3386
|
-
const
|
|
3452
|
+
const tools = this.buildToolSet(itemScopedTools);
|
|
3453
|
+
const callOptions = this.resolveCallOptions(model, guardrails.modelInvocationOptions);
|
|
3454
|
+
const result = await generateText({
|
|
3455
|
+
model: model.languageModel,
|
|
3456
|
+
messages: [...messages],
|
|
3457
|
+
tools,
|
|
3458
|
+
toolChoice: tools ? "auto" : void 0,
|
|
3459
|
+
maxOutputTokens: callOptions.maxOutputTokens,
|
|
3460
|
+
temperature: callOptions.temperature,
|
|
3461
|
+
providerOptions: callOptions.providerOptions,
|
|
3462
|
+
maxRetries: 0
|
|
3463
|
+
});
|
|
3464
|
+
const turnResult = this.extractTurnResult(result);
|
|
3387
3465
|
const finishedAt = /* @__PURE__ */ new Date();
|
|
3388
3466
|
await ctx.nodeState?.markCompleted({
|
|
3389
|
-
nodeId,
|
|
3467
|
+
nodeId: languageModelConnectionNodeId,
|
|
3390
3468
|
activationId: ctx.activationId,
|
|
3391
|
-
inputsByPort,
|
|
3392
|
-
outputs: AgentOutputFactory.fromUnknown({ content:
|
|
3469
|
+
inputsByPort: itemInputsByPort,
|
|
3470
|
+
outputs: AgentOutputFactory.fromUnknown({ content: turnResult.text })
|
|
3393
3471
|
});
|
|
3394
|
-
const content = AgentMessageFactory.extractContent(response);
|
|
3395
3472
|
await span.attachArtifact({
|
|
3396
3473
|
kind: "ai.messages",
|
|
3397
3474
|
contentType: "application/json",
|
|
@@ -3400,26 +3477,26 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3400
3477
|
await span.attachArtifact({
|
|
3401
3478
|
kind: "ai.response",
|
|
3402
3479
|
contentType: "application/json",
|
|
3403
|
-
previewJson:
|
|
3480
|
+
previewJson: turnResult.text
|
|
3404
3481
|
});
|
|
3405
|
-
await this.recordModelUsageMetrics(span,
|
|
3482
|
+
await this.recordModelUsageMetrics(span, turnResult.usage, ctx);
|
|
3406
3483
|
await span.end({
|
|
3407
3484
|
status: "ok",
|
|
3408
3485
|
endedAt: finishedAt
|
|
3409
3486
|
});
|
|
3410
3487
|
await ctx.nodeState?.appendConnectionInvocation({
|
|
3411
3488
|
invocationId,
|
|
3412
|
-
connectionNodeId:
|
|
3489
|
+
connectionNodeId: languageModelConnectionNodeId,
|
|
3413
3490
|
parentAgentNodeId: ctx.nodeId,
|
|
3414
3491
|
parentAgentActivationId: ctx.activationId,
|
|
3415
3492
|
status: "completed",
|
|
3416
3493
|
managedInput: summarizedInput,
|
|
3417
|
-
managedOutput:
|
|
3494
|
+
managedOutput: turnResult.text,
|
|
3418
3495
|
queuedAt: startedAt.toISOString(),
|
|
3419
3496
|
startedAt: startedAt.toISOString(),
|
|
3420
3497
|
finishedAt: finishedAt.toISOString()
|
|
3421
3498
|
});
|
|
3422
|
-
return
|
|
3499
|
+
return turnResult;
|
|
3423
3500
|
} catch (error) {
|
|
3424
3501
|
await span.end({
|
|
3425
3502
|
status: "error",
|
|
@@ -3430,36 +3507,53 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3430
3507
|
error,
|
|
3431
3508
|
invocationId,
|
|
3432
3509
|
startedAt,
|
|
3433
|
-
nodeId,
|
|
3510
|
+
nodeId: languageModelConnectionNodeId,
|
|
3434
3511
|
ctx,
|
|
3435
|
-
inputsByPort,
|
|
3436
|
-
managedInput:
|
|
3512
|
+
inputsByPort: itemInputsByPort,
|
|
3513
|
+
managedInput: summarizedInput
|
|
3437
3514
|
});
|
|
3438
3515
|
}
|
|
3439
3516
|
}
|
|
3440
|
-
|
|
3517
|
+
/**
|
|
3518
|
+
* Structured-output turn: runs `generateText({ output: Output.object({ schema }) })` via the
|
|
3519
|
+
* structured-output runner. We keep this as a separate helper because the runner needs the raw
|
|
3520
|
+
* validated value (not just text) back, and must be able to retry on Zod failures.
|
|
3521
|
+
*/
|
|
3522
|
+
async invokeStructuredTurn(prepared, itemInputsByPort, schema, messages, structuredOptions) {
|
|
3441
3523
|
const invocationId = ConnectionInvocationIdFactory.create();
|
|
3442
3524
|
const startedAt = /* @__PURE__ */ new Date();
|
|
3443
3525
|
const summarizedInput = this.summarizeLlmMessages(messages);
|
|
3526
|
+
const { ctx, model, languageModelConnectionNodeId, guardrails } = prepared;
|
|
3444
3527
|
const span = this.createModelInvocationSpan(ctx, invocationId, startedAt);
|
|
3445
3528
|
await ctx.nodeState?.markQueued({
|
|
3446
|
-
nodeId,
|
|
3529
|
+
nodeId: languageModelConnectionNodeId,
|
|
3447
3530
|
activationId: ctx.activationId,
|
|
3448
|
-
inputsByPort
|
|
3531
|
+
inputsByPort: itemInputsByPort
|
|
3449
3532
|
});
|
|
3450
3533
|
await ctx.nodeState?.markRunning({
|
|
3451
|
-
nodeId,
|
|
3534
|
+
nodeId: languageModelConnectionNodeId,
|
|
3452
3535
|
activationId: ctx.activationId,
|
|
3453
|
-
inputsByPort
|
|
3536
|
+
inputsByPort: itemInputsByPort
|
|
3454
3537
|
});
|
|
3455
3538
|
try {
|
|
3456
|
-
const
|
|
3539
|
+
const callOptions = this.resolveCallOptions(model, guardrails.modelInvocationOptions);
|
|
3540
|
+
const outputSchema = structuredOptions?.strict && !this.isZodSchema(schema) ? Output.object({ schema: jsonSchema(schema) }) : Output.object({ schema });
|
|
3541
|
+
const result = await generateText({
|
|
3542
|
+
model: model.languageModel,
|
|
3543
|
+
messages: [...messages],
|
|
3544
|
+
experimental_output: outputSchema,
|
|
3545
|
+
maxOutputTokens: callOptions.maxOutputTokens,
|
|
3546
|
+
temperature: callOptions.temperature,
|
|
3547
|
+
providerOptions: callOptions.providerOptions,
|
|
3548
|
+
maxRetries: 0
|
|
3549
|
+
});
|
|
3550
|
+
const turnResult = this.extractTurnResult(result);
|
|
3457
3551
|
const finishedAt = /* @__PURE__ */ new Date();
|
|
3458
3552
|
await ctx.nodeState?.markCompleted({
|
|
3459
|
-
nodeId,
|
|
3553
|
+
nodeId: languageModelConnectionNodeId,
|
|
3460
3554
|
activationId: ctx.activationId,
|
|
3461
|
-
inputsByPort,
|
|
3462
|
-
outputs: AgentOutputFactory.fromUnknown(
|
|
3555
|
+
inputsByPort: itemInputsByPort,
|
|
3556
|
+
outputs: AgentOutputFactory.fromUnknown(result.experimental_output)
|
|
3463
3557
|
});
|
|
3464
3558
|
await span.attachArtifact({
|
|
3465
3559
|
kind: "ai.messages",
|
|
@@ -3469,26 +3563,26 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3469
3563
|
await span.attachArtifact({
|
|
3470
3564
|
kind: "ai.response.structured",
|
|
3471
3565
|
contentType: "application/json",
|
|
3472
|
-
previewJson: this.resultToJsonValue(
|
|
3566
|
+
previewJson: this.resultToJsonValue(result.experimental_output)
|
|
3473
3567
|
});
|
|
3474
|
-
await this.recordModelUsageMetrics(span,
|
|
3568
|
+
await this.recordModelUsageMetrics(span, turnResult.usage, ctx);
|
|
3475
3569
|
await span.end({
|
|
3476
3570
|
status: "ok",
|
|
3477
3571
|
endedAt: finishedAt
|
|
3478
3572
|
});
|
|
3479
3573
|
await ctx.nodeState?.appendConnectionInvocation({
|
|
3480
3574
|
invocationId,
|
|
3481
|
-
connectionNodeId:
|
|
3575
|
+
connectionNodeId: languageModelConnectionNodeId,
|
|
3482
3576
|
parentAgentNodeId: ctx.nodeId,
|
|
3483
3577
|
parentAgentActivationId: ctx.activationId,
|
|
3484
3578
|
status: "completed",
|
|
3485
3579
|
managedInput: summarizedInput,
|
|
3486
|
-
managedOutput: this.resultToJsonValue(
|
|
3580
|
+
managedOutput: this.resultToJsonValue(result.experimental_output),
|
|
3487
3581
|
queuedAt: startedAt.toISOString(),
|
|
3488
3582
|
startedAt: startedAt.toISOString(),
|
|
3489
3583
|
finishedAt: finishedAt.toISOString()
|
|
3490
3584
|
});
|
|
3491
|
-
return
|
|
3585
|
+
return result.experimental_output;
|
|
3492
3586
|
} catch (error) {
|
|
3493
3587
|
await span.end({
|
|
3494
3588
|
status: "error",
|
|
@@ -3499,13 +3593,59 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3499
3593
|
error,
|
|
3500
3594
|
invocationId,
|
|
3501
3595
|
startedAt,
|
|
3502
|
-
nodeId,
|
|
3596
|
+
nodeId: languageModelConnectionNodeId,
|
|
3503
3597
|
ctx,
|
|
3504
|
-
inputsByPort,
|
|
3505
|
-
managedInput:
|
|
3598
|
+
inputsByPort: itemInputsByPort,
|
|
3599
|
+
managedInput: summarizedInput
|
|
3506
3600
|
});
|
|
3507
3601
|
}
|
|
3508
3602
|
}
|
|
3603
|
+
isZodSchema(schema) {
|
|
3604
|
+
return typeof schema.parse === "function";
|
|
3605
|
+
}
|
|
3606
|
+
resolveCallOptions(model, overrides) {
|
|
3607
|
+
const defaults = model.defaultCallOptions ?? {};
|
|
3608
|
+
return {
|
|
3609
|
+
maxOutputTokens: overrides?.maxTokens ?? defaults.maxOutputTokens,
|
|
3610
|
+
temperature: defaults.temperature,
|
|
3611
|
+
providerOptions: overrides?.providerOptions ?? defaults.providerOptions
|
|
3612
|
+
};
|
|
3613
|
+
}
|
|
3614
|
+
extractTurnResult(result) {
|
|
3615
|
+
const usage = this.extractUsageFromResult(result);
|
|
3616
|
+
const text = result.text;
|
|
3617
|
+
const toolCalls = result.toolCalls.map((toolCall) => ({
|
|
3618
|
+
id: toolCall.toolCallId,
|
|
3619
|
+
name: toolCall.toolName,
|
|
3620
|
+
input: toolCall.input
|
|
3621
|
+
}));
|
|
3622
|
+
return {
|
|
3623
|
+
assistantMessage: this.extractAssistantMessage(result),
|
|
3624
|
+
text,
|
|
3625
|
+
toolCalls,
|
|
3626
|
+
usage
|
|
3627
|
+
};
|
|
3628
|
+
}
|
|
3629
|
+
extractAssistantMessage(result) {
|
|
3630
|
+
const assistantMessages = result.response.messages.filter((m) => m.role === "assistant");
|
|
3631
|
+
return assistantMessages[assistantMessages.length - 1];
|
|
3632
|
+
}
|
|
3633
|
+
extractUsageFromResult(result) {
|
|
3634
|
+
const usage = result.usage;
|
|
3635
|
+
const inputTokens = this.toFiniteNumber(usage.inputTokens);
|
|
3636
|
+
const outputTokens = this.toFiniteNumber(usage.outputTokens);
|
|
3637
|
+
return {
|
|
3638
|
+
inputTokens,
|
|
3639
|
+
outputTokens,
|
|
3640
|
+
totalTokens: this.toFiniteNumber(usage.totalTokens) ?? (inputTokens !== void 0 && outputTokens !== void 0 ? inputTokens + outputTokens : void 0),
|
|
3641
|
+
cachedInputTokens: this.toFiniteNumber(usage.cachedInputTokens),
|
|
3642
|
+
reasoningTokens: this.toFiniteNumber(usage.reasoningTokens)
|
|
3643
|
+
};
|
|
3644
|
+
}
|
|
3645
|
+
toFiniteNumber(value) {
|
|
3646
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return void 0;
|
|
3647
|
+
return value;
|
|
3648
|
+
}
|
|
3509
3649
|
createModelInvocationSpan(ctx, invocationId, startedAt) {
|
|
3510
3650
|
return ctx.telemetry.startChildSpan({
|
|
3511
3651
|
name: "gen_ai.chat.completion",
|
|
@@ -3518,9 +3658,15 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3518
3658
|
}
|
|
3519
3659
|
});
|
|
3520
3660
|
}
|
|
3521
|
-
async recordModelUsageMetrics(span,
|
|
3522
|
-
const
|
|
3523
|
-
|
|
3661
|
+
async recordModelUsageMetrics(span, usage, ctx) {
|
|
3662
|
+
const entries = [
|
|
3663
|
+
[GenAiTelemetryAttributeNames.usageInputTokens, usage.inputTokens],
|
|
3664
|
+
[GenAiTelemetryAttributeNames.usageOutputTokens, usage.outputTokens],
|
|
3665
|
+
[GenAiTelemetryAttributeNames.usageTotalTokens, usage.totalTokens],
|
|
3666
|
+
[GenAiTelemetryAttributeNames.usageCacheReadInputTokens, usage.cachedInputTokens],
|
|
3667
|
+
[GenAiTelemetryAttributeNames.usageReasoningTokens, usage.reasoningTokens]
|
|
3668
|
+
];
|
|
3669
|
+
for (const [name, value] of entries) {
|
|
3524
3670
|
if (value === void 0) continue;
|
|
3525
3671
|
await span.recordMetric({
|
|
3526
3672
|
name,
|
|
@@ -3535,93 +3681,28 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3535
3681
|
const provider = ctx.config.chatModel.provider;
|
|
3536
3682
|
const pricingKey = ctx.config.chatModel.modelName;
|
|
3537
3683
|
if (!provider || !pricingKey) return;
|
|
3538
|
-
|
|
3539
|
-
const outputTokens = usage[GenAiTelemetryAttributeNames.usageOutputTokens];
|
|
3540
|
-
if (inputTokens !== void 0) await costTracking.captureUsage({
|
|
3684
|
+
if (usage.inputTokens !== void 0) await costTracking.captureUsage({
|
|
3541
3685
|
component: "chat",
|
|
3542
3686
|
provider,
|
|
3543
3687
|
operation: "completion.input",
|
|
3544
3688
|
pricingKey,
|
|
3545
3689
|
usageUnit: "input_tokens",
|
|
3546
|
-
quantity: inputTokens,
|
|
3690
|
+
quantity: usage.inputTokens,
|
|
3547
3691
|
modelName: pricingKey
|
|
3548
3692
|
});
|
|
3549
|
-
if (outputTokens !== void 0) await costTracking.captureUsage({
|
|
3693
|
+
if (usage.outputTokens !== void 0) await costTracking.captureUsage({
|
|
3550
3694
|
component: "chat",
|
|
3551
3695
|
provider,
|
|
3552
3696
|
operation: "completion.output",
|
|
3553
3697
|
pricingKey,
|
|
3554
3698
|
usageUnit: "output_tokens",
|
|
3555
|
-
quantity: outputTokens,
|
|
3699
|
+
quantity: usage.outputTokens,
|
|
3556
3700
|
modelName: pricingKey
|
|
3557
3701
|
});
|
|
3558
3702
|
}
|
|
3559
3703
|
resolveChatModelName(chatModel$1) {
|
|
3560
3704
|
return chatModel$1.modelName ?? chatModel$1.name;
|
|
3561
3705
|
}
|
|
3562
|
-
extractModelUsageMetrics(response) {
|
|
3563
|
-
const usage = this.extractUsageObject(response);
|
|
3564
|
-
const inputTokens = this.readUsageNumber(usage, [
|
|
3565
|
-
"input_tokens",
|
|
3566
|
-
"inputTokens",
|
|
3567
|
-
"prompt_tokens",
|
|
3568
|
-
"promptTokens"
|
|
3569
|
-
]);
|
|
3570
|
-
const outputTokens = this.readUsageNumber(usage, [
|
|
3571
|
-
"output_tokens",
|
|
3572
|
-
"outputTokens",
|
|
3573
|
-
"completion_tokens",
|
|
3574
|
-
"completionTokens"
|
|
3575
|
-
]);
|
|
3576
|
-
const totalTokens = this.readUsageNumber(usage, ["total_tokens", "totalTokens"]) ?? (inputTokens !== void 0 && outputTokens !== void 0 ? inputTokens + outputTokens : void 0);
|
|
3577
|
-
const cachedInputTokens = this.readUsageNumber(usage, [
|
|
3578
|
-
"cache_read_input_tokens",
|
|
3579
|
-
"cacheReadInputTokens",
|
|
3580
|
-
"input_token_details.cached_tokens"
|
|
3581
|
-
]);
|
|
3582
|
-
const reasoningTokens = this.readUsageNumber(usage, [
|
|
3583
|
-
"reasoning_tokens",
|
|
3584
|
-
"reasoningTokens",
|
|
3585
|
-
"output_token_details.reasoning_tokens"
|
|
3586
|
-
]);
|
|
3587
|
-
return {
|
|
3588
|
-
[GenAiTelemetryAttributeNames.usageInputTokens]: inputTokens,
|
|
3589
|
-
[GenAiTelemetryAttributeNames.usageOutputTokens]: outputTokens,
|
|
3590
|
-
[GenAiTelemetryAttributeNames.usageTotalTokens]: totalTokens,
|
|
3591
|
-
[GenAiTelemetryAttributeNames.usageCacheReadInputTokens]: cachedInputTokens,
|
|
3592
|
-
[GenAiTelemetryAttributeNames.usageReasoningTokens]: reasoningTokens
|
|
3593
|
-
};
|
|
3594
|
-
}
|
|
3595
|
-
extractUsageObject(response) {
|
|
3596
|
-
if (!this.isRecord(response)) return;
|
|
3597
|
-
const usageMetadata = response["usage_metadata"];
|
|
3598
|
-
if (this.isRecord(usageMetadata)) return usageMetadata;
|
|
3599
|
-
const responseMetadata = response["response_metadata"];
|
|
3600
|
-
if (this.isRecord(responseMetadata)) {
|
|
3601
|
-
const tokenUsage = responseMetadata["tokenUsage"];
|
|
3602
|
-
if (this.isRecord(tokenUsage)) return tokenUsage;
|
|
3603
|
-
const usage = responseMetadata["usage"];
|
|
3604
|
-
if (this.isRecord(usage)) return usage;
|
|
3605
|
-
}
|
|
3606
|
-
}
|
|
3607
|
-
readUsageNumber(source, keys) {
|
|
3608
|
-
for (const key of keys) {
|
|
3609
|
-
const value = this.readNestedUsageValue(source, key);
|
|
3610
|
-
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
3611
|
-
}
|
|
3612
|
-
}
|
|
3613
|
-
readNestedUsageValue(source, dottedKey) {
|
|
3614
|
-
if (!source) return;
|
|
3615
|
-
let current = source;
|
|
3616
|
-
for (const segment of dottedKey.split(".")) {
|
|
3617
|
-
if (!this.isRecord(current)) return;
|
|
3618
|
-
current = current[segment];
|
|
3619
|
-
}
|
|
3620
|
-
return current;
|
|
3621
|
-
}
|
|
3622
|
-
isRecord(value) {
|
|
3623
|
-
return typeof value === "object" && value !== null;
|
|
3624
|
-
}
|
|
3625
3706
|
async markQueuedTools(plannedToolCalls, ctx) {
|
|
3626
3707
|
for (const plannedToolCall of plannedToolCalls) await ctx.nodeState?.markQueued({
|
|
3627
3708
|
nodeId: plannedToolCall.nodeId,
|
|
@@ -3681,7 +3762,7 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3681
3762
|
};
|
|
3682
3763
|
}
|
|
3683
3764
|
resultToJsonValue(value) {
|
|
3684
|
-
if (value === void 0) return;
|
|
3765
|
+
if (value === void 0) return void 0;
|
|
3685
3766
|
const json = JSON.stringify(value);
|
|
3686
3767
|
return JSON.parse(json);
|
|
3687
3768
|
}
|
|
@@ -3696,7 +3777,7 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3696
3777
|
resolveToolRuntime(config$1) {
|
|
3697
3778
|
if (this.isNodeBackedToolConfig(config$1)) {
|
|
3698
3779
|
const inputSchema = config$1.getInputSchema();
|
|
3699
|
-
if (inputSchema == null) throw new Error(`AIAgent tool "${config$1.name}": node-backed tool is missing inputSchema (cannot build
|
|
3780
|
+
if (inputSchema == null) throw new Error(`AIAgent tool "${config$1.name}": node-backed tool is missing inputSchema (cannot build AI SDK tool).`);
|
|
3700
3781
|
return {
|
|
3701
3782
|
defaultDescription: `Run workflow node "${config$1.node.name ?? config$1.name}" as an AI tool.`,
|
|
3702
3783
|
inputSchema,
|
|
@@ -3705,7 +3786,7 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3705
3786
|
}
|
|
3706
3787
|
if (this.isCallableToolConfig(config$1)) {
|
|
3707
3788
|
const inputSchema = config$1.getInputSchema();
|
|
3708
|
-
if (inputSchema == null) throw new Error(`AIAgent tool "${config$1.name}": callable tool is missing inputSchema (cannot build
|
|
3789
|
+
if (inputSchema == null) throw new Error(`AIAgent tool "${config$1.name}": callable tool is missing inputSchema (cannot build AI SDK tool).`);
|
|
3709
3790
|
return {
|
|
3710
3791
|
defaultDescription: config$1.description ?? `Callable tool "${config$1.name}".`,
|
|
3711
3792
|
inputSchema,
|
|
@@ -3723,17 +3804,9 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3723
3804
|
execute: async (args) => await Promise.resolve(tool.execute(args))
|
|
3724
3805
|
};
|
|
3725
3806
|
}
|
|
3726
|
-
/**
|
|
3727
|
-
* Consumer apps can resolve two copies of `@codemation/core`, breaking `instanceof NodeBackedToolConfig` and
|
|
3728
|
-
* sending node-backed tools down the plugin-tool branch with `inputSchema: undefined` (LangChain then crashes in
|
|
3729
|
-
* json-schema validation). {@link NodeBackedToolConfig#toolKind} is stable across copies.
|
|
3730
|
-
*/
|
|
3731
3807
|
isNodeBackedToolConfig(config$1) {
|
|
3732
3808
|
return config$1 instanceof NodeBackedToolConfig || typeof config$1 === "object" && config$1 !== null && config$1.toolKind === "nodeBacked";
|
|
3733
3809
|
}
|
|
3734
|
-
/**
|
|
3735
|
-
* Callable tools use {@link CallableToolConfig#toolKind} for cross-package / JSON round-trip safety.
|
|
3736
|
-
*/
|
|
3737
3810
|
isCallableToolConfig(config$1) {
|
|
3738
3811
|
return config$1 instanceof CallableToolConfig || typeof config$1 === "object" && config$1 !== null && config$1.toolKind === "callable";
|
|
3739
3812
|
}
|
|
@@ -4824,5 +4897,5 @@ var ConnectionCredentialNodeConfigFactory = class {
|
|
|
4824
4897
|
};
|
|
4825
4898
|
|
|
4826
4899
|
//#endregion
|
|
4827
|
-
export { AIAgent, AIAgentConnectionWorkflowExpander, AIAgentExecutionHelpersFactory, AIAgentNode, AgentItemPortMap, AgentMessageFactory, AgentOutputFactory, AgentStructuredOutputRepairPromptFactory, AgentStructuredOutputRunner, AgentToolCallPortMap, AgentToolErrorClassifier, AgentToolExecutionCoordinator, AgentToolRepairExhaustedError, AgentToolRepairPolicy, Aggregate, AggregateNode, Callback, CallbackNode, CallbackResultNormalizer, ConnectionCredentialExecutionContextFactory, ConnectionCredentialNode, ConnectionCredentialNodeConfig, ConnectionCredentialNodeConfigFactory, Filter, FilterNode, HttpRequest, HttpRequestNode, If, IfNode, ManualTrigger, ManualTriggerNode, MapData, MapDataNode, Merge, MergeNode, NoOp, NoOpNode, OpenAIChatModelConfig, OpenAIChatModelFactory,
|
|
4900
|
+
export { AIAgent, AIAgentConnectionWorkflowExpander, AIAgentExecutionHelpersFactory, AIAgentNode, AgentItemPortMap, AgentMessageFactory, AgentOutputFactory, AgentStructuredOutputRepairPromptFactory, AgentStructuredOutputRunner, AgentToolCallPortMap, AgentToolErrorClassifier, AgentToolExecutionCoordinator, AgentToolRepairExhaustedError, AgentToolRepairPolicy, Aggregate, AggregateNode, Callback, CallbackNode, CallbackResultNormalizer, ConnectionCredentialExecutionContextFactory, ConnectionCredentialNode, ConnectionCredentialNodeConfig, ConnectionCredentialNodeConfigFactory, Filter, FilterNode, HttpRequest, HttpRequestNode, If, IfNode, ManualTrigger, ManualTriggerNode, MapData, MapDataNode, Merge, MergeNode, NoOp, NoOpNode, OpenAIChatModelConfig, OpenAIChatModelFactory, OpenAiChatModelPresets, OpenAiStrictJsonSchemaFactory, Split, SplitNode, SubWorkflow, SubWorkflowNode, Switch, SwitchNode, Wait, WaitDuration, WaitNode, WebhookRespondNowAndContinueError, WebhookRespondNowError, WebhookTrigger, WebhookTriggerNode, WorkflowAuthoringBuilder, WorkflowBranchBuilder, WorkflowChain, createWorkflowBuilder, openAiChatModelPresets, registerCoreNodes, workflow };
|
|
4828
4901
|
//# sourceMappingURL=index.js.map
|