@codemation/core-nodes 0.4.3 → 1.0.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.
- package/CHANGELOG.md +67 -0
- package/dist/index.cjs +489 -415
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +229 -121
- package/dist/index.d.ts +229 -121
- package/dist/index.js +485 -408
- 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/WebhookTriggerFactory.ts +1 -1
- package/src/nodes/aggregate.ts +1 -1
- package/src/nodes/aiAgentSupport.types.ts +7 -2
- package/src/nodes/httpRequest.ts +1 -0
- package/src/nodes/if.ts +1 -1
- package/src/nodes/mapData.ts +1 -0
- package/src/nodes/merge.ts +1 -1
- package/src/nodes/noOp.ts +1 -0
- package/src/nodes/split.ts +1 -1
- package/src/nodes/wait.ts +1 -0
- package/src/chatModels/OpenAIStructuredOutputMethodFactory.ts +0 -46
package/dist/index.cjs
CHANGED
|
@@ -23,18 +23,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
//#endregion
|
|
24
24
|
let __codemation_core = require("@codemation/core");
|
|
25
25
|
__codemation_core = __toESM(__codemation_core);
|
|
26
|
-
let
|
|
27
|
-
|
|
28
|
-
let __langchain_core_messages = require("@langchain/core/messages");
|
|
29
|
-
__langchain_core_messages = __toESM(__langchain_core_messages);
|
|
30
|
-
let __langchain_core_utils_types = require("@langchain/core/utils/types");
|
|
31
|
-
__langchain_core_utils_types = __toESM(__langchain_core_utils_types);
|
|
32
|
-
let __langchain_core_utils_json_schema = require("@langchain/core/utils/json_schema");
|
|
33
|
-
__langchain_core_utils_json_schema = __toESM(__langchain_core_utils_json_schema);
|
|
34
|
-
let __langchain_core_tools = require("@langchain/core/tools");
|
|
35
|
-
__langchain_core_tools = __toESM(__langchain_core_tools);
|
|
26
|
+
let __ai_sdk_openai = require("@ai-sdk/openai");
|
|
27
|
+
__ai_sdk_openai = __toESM(__ai_sdk_openai);
|
|
36
28
|
let __codemation_core_bootstrap = require("@codemation/core/bootstrap");
|
|
37
29
|
__codemation_core_bootstrap = __toESM(__codemation_core_bootstrap);
|
|
30
|
+
let ai = require("ai");
|
|
31
|
+
ai = __toESM(ai);
|
|
38
32
|
|
|
39
33
|
//#region \0@oxc-project+runtime@0.95.0/helpers/decorate.js
|
|
40
34
|
function __decorate(decorators, target, key, desc) {
|
|
@@ -49,166 +43,21 @@ function __decorate(decorators, target, key, desc) {
|
|
|
49
43
|
let OpenAIChatModelFactory = class OpenAIChatModelFactory$1 {
|
|
50
44
|
async create(args) {
|
|
51
45
|
const session = await args.ctx.getCredential(args.config.credentialSlotKey);
|
|
52
|
-
return new __langchain_openai.ChatOpenAI({
|
|
53
|
-
apiKey: session.apiKey,
|
|
54
|
-
model: args.config.model,
|
|
55
|
-
temperature: args.config.options?.temperature,
|
|
56
|
-
maxTokens: args.config.options?.maxTokens,
|
|
57
|
-
configuration: session.baseUrl ? { baseURL: session.baseUrl } : void 0
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
OpenAIChatModelFactory = __decorate([(0, __codemation_core.chatModel)({ packageName: "@codemation/core-nodes" })], OpenAIChatModelFactory);
|
|
62
|
-
|
|
63
|
-
//#endregion
|
|
64
|
-
//#region src/chatModels/OpenAIStructuredOutputMethodFactory.ts
|
|
65
|
-
var _OpenAIStructuredOutputMethodFactory;
|
|
66
|
-
let OpenAIStructuredOutputMethodFactory = class OpenAIStructuredOutputMethodFactory$1 {
|
|
67
|
-
static {
|
|
68
|
-
_OpenAIStructuredOutputMethodFactory = this;
|
|
69
|
-
}
|
|
70
|
-
static isoDatePattern = /^\d{4}-\d{2}-\d{2}$/;
|
|
71
|
-
create(chatModelConfig) {
|
|
72
|
-
if (chatModelConfig.type !== OpenAIChatModelFactory) return;
|
|
73
|
-
const model = this.readModelName(chatModelConfig);
|
|
74
|
-
if (!model) return {
|
|
75
|
-
method: "functionCalling",
|
|
76
|
-
strict: true
|
|
77
|
-
};
|
|
78
|
-
return {
|
|
79
|
-
method: this.supportsJsonSchema(model) ? "jsonSchema" : "functionCalling",
|
|
80
|
-
strict: true
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
readModelName(chatModelConfig) {
|
|
84
|
-
const candidate = chatModelConfig;
|
|
85
|
-
return typeof candidate.model === "string" ? candidate.model : void 0;
|
|
86
|
-
}
|
|
87
|
-
supportsJsonSchema(model) {
|
|
88
|
-
if (model === "gpt-4o" || model === "gpt-4o-mini") return true;
|
|
89
|
-
return this.supportsSnapshotAtOrAfter(model, "gpt-4o-", "2024-08-06") || this.supportsSnapshotAtOrAfter(model, "gpt-4o-mini-", "2024-07-18");
|
|
90
|
-
}
|
|
91
|
-
supportsSnapshotAtOrAfter(model, prefix, minimumSnapshotDate) {
|
|
92
|
-
if (!model.startsWith(prefix)) return false;
|
|
93
|
-
const snapshotDate = model.slice(prefix.length);
|
|
94
|
-
return _OpenAIStructuredOutputMethodFactory.isoDatePattern.test(snapshotDate) && snapshotDate >= minimumSnapshotDate;
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
OpenAIStructuredOutputMethodFactory = _OpenAIStructuredOutputMethodFactory = __decorate([(0, __codemation_core.injectable)()], OpenAIStructuredOutputMethodFactory);
|
|
98
|
-
|
|
99
|
-
//#endregion
|
|
100
|
-
//#region src/chatModels/openAiChatModelConfig.ts
|
|
101
|
-
var OpenAIChatModelConfig = class {
|
|
102
|
-
type = OpenAIChatModelFactory;
|
|
103
|
-
presentation;
|
|
104
|
-
provider = "openai";
|
|
105
|
-
modelName;
|
|
106
|
-
constructor(name, model, credentialSlotKey = "openai", presentationIn, options) {
|
|
107
|
-
this.name = name;
|
|
108
|
-
this.model = model;
|
|
109
|
-
this.credentialSlotKey = credentialSlotKey;
|
|
110
|
-
this.options = options;
|
|
111
|
-
this.modelName = model;
|
|
112
|
-
this.presentation = presentationIn ?? {
|
|
113
|
-
icon: "builtin:openai",
|
|
114
|
-
label: name
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
getCredentialRequirements() {
|
|
118
|
-
return [{
|
|
119
|
-
slotKey: this.credentialSlotKey,
|
|
120
|
-
label: "OpenAI API key",
|
|
121
|
-
acceptedTypes: ["openai.apiKey"]
|
|
122
|
-
}];
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
//#endregion
|
|
127
|
-
//#region src/chatModels/OpenAiChatModelPresetsFactory.ts
|
|
128
|
-
/**
|
|
129
|
-
* Default OpenAI chat model configs for scaffolds and demos (icon + label match {@link OpenAIChatModelConfig} defaults).
|
|
130
|
-
* Prefer importing {@link openAiChatModelPresets} from here or from the consumer template re-export
|
|
131
|
-
* instead of repeating {@link OpenAIChatModelConfig} construction in app workflows.
|
|
132
|
-
*/
|
|
133
|
-
var OpenAiChatModelPresets = class {
|
|
134
|
-
demoGpt4oMini = new OpenAIChatModelConfig("OpenAI", "gpt-4o-mini");
|
|
135
|
-
demoGpt41 = new OpenAIChatModelConfig("OpenAI", "gpt-4.1");
|
|
136
|
-
};
|
|
137
|
-
const openAiChatModelPresets = new OpenAiChatModelPresets();
|
|
138
|
-
|
|
139
|
-
//#endregion
|
|
140
|
-
//#region src/nodes/AgentMessageFactory.ts
|
|
141
|
-
var AgentMessageFactory = class {
|
|
142
|
-
static createPromptMessages(messages) {
|
|
143
|
-
return messages.map((message) => this.createPromptMessage(message));
|
|
144
|
-
}
|
|
145
|
-
static createSystemPrompt(systemMessage) {
|
|
146
|
-
return new __langchain_core_messages.SystemMessage(systemMessage);
|
|
147
|
-
}
|
|
148
|
-
static createUserPrompt(prompt) {
|
|
149
|
-
return new __langchain_core_messages.HumanMessage(prompt);
|
|
150
|
-
}
|
|
151
|
-
static createAssistantPrompt(prompt) {
|
|
152
|
-
return new __langchain_core_messages.AIMessage(prompt);
|
|
153
|
-
}
|
|
154
|
-
static createToolMessage(toolCallId, content) {
|
|
155
|
-
return new __langchain_core_messages.ToolMessage({
|
|
156
|
-
tool_call_id: toolCallId,
|
|
157
|
-
content
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
static extractContent(message) {
|
|
161
|
-
if (typeof message === "string") return message;
|
|
162
|
-
if (!this.isRecord(message)) return String(message);
|
|
163
|
-
const content = message.content;
|
|
164
|
-
if (typeof content === "string") return content;
|
|
165
|
-
if (Array.isArray(content)) return content.map((part) => {
|
|
166
|
-
if (typeof part === "string") return part;
|
|
167
|
-
if (this.isRecord(part) && typeof part.text === "string") return part.text;
|
|
168
|
-
return JSON.stringify(part);
|
|
169
|
-
}).join("\n");
|
|
170
|
-
return JSON.stringify(content);
|
|
171
|
-
}
|
|
172
|
-
static extractToolCalls(message) {
|
|
173
|
-
if (!this.isRecord(message)) return [];
|
|
174
|
-
const toolCalls = message.tool_calls;
|
|
175
|
-
if (!Array.isArray(toolCalls)) return [];
|
|
176
|
-
return toolCalls.filter((toolCall) => this.isRecord(toolCall) && typeof toolCall.name === "string").map((toolCall) => ({
|
|
177
|
-
id: typeof toolCall.id === "string" ? toolCall.id : void 0,
|
|
178
|
-
name: toolCall.name,
|
|
179
|
-
input: this.isRecord(toolCall) && "args" in toolCall ? toolCall.args : void 0
|
|
180
|
-
}));
|
|
181
|
-
}
|
|
182
|
-
static isRecord(value) {
|
|
183
|
-
return typeof value === "object" && value !== null;
|
|
184
|
-
}
|
|
185
|
-
static createPromptMessage(message) {
|
|
186
|
-
if (message.role === "system") return this.createSystemPrompt(message.content);
|
|
187
|
-
if (message.role === "assistant") return this.createAssistantPrompt(message.content);
|
|
188
|
-
return this.createUserPrompt(message.content);
|
|
189
|
-
}
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
//#endregion
|
|
193
|
-
//#region src/nodes/AgentOutputFactory.ts
|
|
194
|
-
var AgentOutputFactory = class {
|
|
195
|
-
static fromUnknown(value) {
|
|
196
|
-
return { main: [{ json: value }] };
|
|
197
|
-
}
|
|
198
|
-
static replaceJson(item, value) {
|
|
199
46
|
return {
|
|
200
|
-
|
|
201
|
-
|
|
47
|
+
languageModel: (0, __ai_sdk_openai.createOpenAI)({
|
|
48
|
+
apiKey: session.apiKey,
|
|
49
|
+
baseURL: session.baseUrl
|
|
50
|
+
}).chat(args.config.model),
|
|
51
|
+
modelName: args.config.model,
|
|
52
|
+
provider: "openai",
|
|
53
|
+
defaultCallOptions: {
|
|
54
|
+
maxOutputTokens: args.config.options?.maxTokens,
|
|
55
|
+
temperature: args.config.options?.temperature
|
|
56
|
+
}
|
|
202
57
|
};
|
|
203
58
|
}
|
|
204
|
-
static fromAgentContent(content) {
|
|
205
|
-
try {
|
|
206
|
-
return JSON.parse(content);
|
|
207
|
-
} catch {
|
|
208
|
-
return { output: content };
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
59
|
};
|
|
60
|
+
OpenAIChatModelFactory = __decorate([(0, __codemation_core.chatModel)({ packageName: "@codemation/core-nodes" })], OpenAIChatModelFactory);
|
|
212
61
|
|
|
213
62
|
//#endregion
|
|
214
63
|
//#region ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/core/core.js
|
|
@@ -2130,53 +1979,35 @@ let AIAgentExecutionHelpersFactory = class AIAgentExecutionHelpersFactory$1 {
|
|
|
2130
1979
|
createConnectionCredentialExecutionContextFactory(credentialSessions) {
|
|
2131
1980
|
return new ConnectionCredentialExecutionContextFactory(credentialSessions);
|
|
2132
1981
|
}
|
|
2133
|
-
createDynamicStructuredTool(entry, toolCredentialContext, item, itemIndex, items) {
|
|
2134
|
-
if (entry.runtime.inputSchema == null) throw new Error(`Cannot create LangChain tool "${entry.config.name}": missing inputSchema (broken tool runtime resolution).`);
|
|
2135
|
-
const schemaForOpenAi = this.createJsonSchemaRecord(entry.runtime.inputSchema, {
|
|
2136
|
-
schemaName: entry.config.name,
|
|
2137
|
-
requireObjectRoot: true
|
|
2138
|
-
});
|
|
2139
|
-
return new __langchain_core_tools.DynamicStructuredTool({
|
|
2140
|
-
name: entry.config.name,
|
|
2141
|
-
description: entry.config.description ?? entry.runtime.defaultDescription,
|
|
2142
|
-
schema: schemaForOpenAi,
|
|
2143
|
-
func: async (input) => {
|
|
2144
|
-
const result = await entry.runtime.execute({
|
|
2145
|
-
config: entry.config,
|
|
2146
|
-
input,
|
|
2147
|
-
ctx: toolCredentialContext,
|
|
2148
|
-
item,
|
|
2149
|
-
itemIndex,
|
|
2150
|
-
items
|
|
2151
|
-
});
|
|
2152
|
-
return JSON.stringify(result);
|
|
2153
|
-
}
|
|
2154
|
-
});
|
|
2155
|
-
}
|
|
2156
1982
|
/**
|
|
2157
|
-
* Produces a plain JSON Schema object
|
|
2158
|
-
* -
|
|
2159
|
-
*
|
|
2160
|
-
*
|
|
2161
|
-
*
|
|
2162
|
-
* -
|
|
1983
|
+
* Produces a plain JSON Schema object (`draft-07`) from a Zod schema, as needed by
|
|
1984
|
+
* OpenAI tool-parameter schemas and the structured-output repair prompt.
|
|
1985
|
+
* - Prefers the schema's **instance** `toJSONSchema(...)` method so we stay inside the Zod
|
|
1986
|
+
* instance that created the schema (works across consumer/framework tsx namespaces — see
|
|
1987
|
+
* {@link ZodInstanceToJsonSchema}). Falls back to the framework-imported module function.
|
|
1988
|
+
* - Strips root `$schema` (OpenAI ignores it).
|
|
1989
|
+
* - Sanitizes `required` for cfworker json-schema compatibility (must be a string array or absent).
|
|
2163
1990
|
*/
|
|
2164
1991
|
createJsonSchemaRecord(inputSchema, options) {
|
|
2165
|
-
const
|
|
2166
|
-
|
|
2167
|
-
if (
|
|
2168
|
-
else {
|
|
2169
|
-
converted = (0, __langchain_core_utils_json_schema.toJsonSchema)(inputSchema);
|
|
2170
|
-
if ((0, __langchain_core_utils_types.isInteropZodSchema)(converted)) converted = toJSONSchema(inputSchema, draft07Params);
|
|
2171
|
-
}
|
|
2172
|
-
const { $schema: _draftSchemaOmitted,...rest } = converted;
|
|
2173
|
-
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)}).`);
|
|
2174
|
-
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)}).`);
|
|
1992
|
+
const { $schema: _draftSchemaOmitted,...rest } = this.convertZodSchemaToJsonSchema(inputSchema, { target: "draft-07" });
|
|
1993
|
+
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)}).`);
|
|
1994
|
+
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)}).`);
|
|
2175
1995
|
if (options.requireObjectRoot && rest.properties === void 0) rest.properties = {};
|
|
2176
1996
|
this.sanitizeJsonSchemaRequiredKeywordsForCfworker(rest);
|
|
2177
1997
|
return rest;
|
|
2178
1998
|
}
|
|
2179
1999
|
/**
|
|
2000
|
+
* Runs Zod's `toJSONSchema` via the schema's own instance method when available, so consumer
|
|
2001
|
+
* schemas loaded under a different tsx namespace still convert correctly. If the caller handed us
|
|
2002
|
+
* a payload that lacks that method (e.g. a plain JSON Schema record or a Zod instance whose
|
|
2003
|
+
* prototype was stripped), we fall back to the framework-bundled module function.
|
|
2004
|
+
*/
|
|
2005
|
+
convertZodSchemaToJsonSchema(inputSchema, params) {
|
|
2006
|
+
const candidate = inputSchema.toJSONSchema;
|
|
2007
|
+
if (typeof candidate === "function") return candidate.call(inputSchema, params);
|
|
2008
|
+
return toJSONSchema(inputSchema, params);
|
|
2009
|
+
}
|
|
2010
|
+
/**
|
|
2180
2011
|
* `@cfworker/json-schema` iterates `schema.required` with `for...of`; it must be a string array or absent.
|
|
2181
2012
|
*/
|
|
2182
2013
|
sanitizeJsonSchemaRequiredKeywordsForCfworker(node$16) {
|
|
@@ -2223,6 +2054,214 @@ function __decorateParam(paramIndex, decorator) {
|
|
|
2223
2054
|
};
|
|
2224
2055
|
}
|
|
2225
2056
|
|
|
2057
|
+
//#endregion
|
|
2058
|
+
//#region src/chatModels/OpenAiStrictJsonSchemaFactory.ts
|
|
2059
|
+
var _ref$5;
|
|
2060
|
+
let OpenAiStrictJsonSchemaFactory = class OpenAiStrictJsonSchemaFactory$1 {
|
|
2061
|
+
constructor(executionHelpers) {
|
|
2062
|
+
this.executionHelpers = executionHelpers;
|
|
2063
|
+
}
|
|
2064
|
+
createStructuredOutputRecord(schema, options) {
|
|
2065
|
+
const record = this.executionHelpers.createJsonSchemaRecord(schema, {
|
|
2066
|
+
schemaName: options.schemaName,
|
|
2067
|
+
requireObjectRoot: false
|
|
2068
|
+
});
|
|
2069
|
+
this.strictifyRecursive(record);
|
|
2070
|
+
if (options.title !== void 0) record.title = options.title;
|
|
2071
|
+
return record;
|
|
2072
|
+
}
|
|
2073
|
+
strictifyRecursive(node$16) {
|
|
2074
|
+
if (!node$16 || typeof node$16 !== "object" || Array.isArray(node$16)) return;
|
|
2075
|
+
const o = node$16;
|
|
2076
|
+
this.stripOpenAiRejectedKeywords(o);
|
|
2077
|
+
if (this.isObjectNode(o)) {
|
|
2078
|
+
const props = this.readPropertiesObject(o);
|
|
2079
|
+
o.properties = props;
|
|
2080
|
+
o.additionalProperties = false;
|
|
2081
|
+
o.required = Object.keys(props);
|
|
2082
|
+
for (const value of Object.values(props)) this.strictifyRecursive(value);
|
|
2083
|
+
}
|
|
2084
|
+
this.recurseIntoComposites(o);
|
|
2085
|
+
}
|
|
2086
|
+
stripOpenAiRejectedKeywords(o) {
|
|
2087
|
+
delete o["$schema"];
|
|
2088
|
+
delete o["unevaluatedProperties"];
|
|
2089
|
+
delete o["default"];
|
|
2090
|
+
}
|
|
2091
|
+
isObjectNode(o) {
|
|
2092
|
+
const typeIsObject = o.type === "object" || Array.isArray(o.type) && o.type.includes("object");
|
|
2093
|
+
const hasObjectProperties = o.properties !== void 0 && typeof o.properties === "object" && !Array.isArray(o.properties);
|
|
2094
|
+
return typeIsObject || hasObjectProperties;
|
|
2095
|
+
}
|
|
2096
|
+
readPropertiesObject(o) {
|
|
2097
|
+
if (o.properties && typeof o.properties === "object" && !Array.isArray(o.properties)) return o.properties;
|
|
2098
|
+
return {};
|
|
2099
|
+
}
|
|
2100
|
+
recurseIntoComposites(o) {
|
|
2101
|
+
for (const key of [
|
|
2102
|
+
"allOf",
|
|
2103
|
+
"anyOf",
|
|
2104
|
+
"oneOf",
|
|
2105
|
+
"prefixItems"
|
|
2106
|
+
]) {
|
|
2107
|
+
const branch = o[key];
|
|
2108
|
+
if (Array.isArray(branch)) for (const sub of branch) this.strictifyRecursive(sub);
|
|
2109
|
+
}
|
|
2110
|
+
if (o.not) this.strictifyRecursive(o.not);
|
|
2111
|
+
if (o.items) if (Array.isArray(o.items)) for (const sub of o.items) this.strictifyRecursive(sub);
|
|
2112
|
+
else this.strictifyRecursive(o.items);
|
|
2113
|
+
for (const key of [
|
|
2114
|
+
"if",
|
|
2115
|
+
"then",
|
|
2116
|
+
"else"
|
|
2117
|
+
]) if (o[key]) this.strictifyRecursive(o[key]);
|
|
2118
|
+
for (const key of ["$defs", "definitions"]) {
|
|
2119
|
+
const defs = o[key];
|
|
2120
|
+
if (defs && typeof defs === "object" && !Array.isArray(defs)) for (const sub of Object.values(defs)) this.strictifyRecursive(sub);
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
};
|
|
2124
|
+
OpenAiStrictJsonSchemaFactory = __decorate([
|
|
2125
|
+
(0, __codemation_core.injectable)(),
|
|
2126
|
+
__decorateParam(0, (0, __codemation_core.inject)(AIAgentExecutionHelpersFactory)),
|
|
2127
|
+
__decorateMetadata("design:paramtypes", [typeof (_ref$5 = typeof AIAgentExecutionHelpersFactory !== "undefined" && AIAgentExecutionHelpersFactory) === "function" ? _ref$5 : Object])
|
|
2128
|
+
], OpenAiStrictJsonSchemaFactory);
|
|
2129
|
+
|
|
2130
|
+
//#endregion
|
|
2131
|
+
//#region src/chatModels/openAiChatModelConfig.ts
|
|
2132
|
+
var OpenAIChatModelConfig = class {
|
|
2133
|
+
type = OpenAIChatModelFactory;
|
|
2134
|
+
presentation;
|
|
2135
|
+
provider = "openai";
|
|
2136
|
+
modelName;
|
|
2137
|
+
constructor(name, model, credentialSlotKey = "openai", presentationIn, options) {
|
|
2138
|
+
this.name = name;
|
|
2139
|
+
this.model = model;
|
|
2140
|
+
this.credentialSlotKey = credentialSlotKey;
|
|
2141
|
+
this.options = options;
|
|
2142
|
+
this.modelName = model;
|
|
2143
|
+
this.presentation = presentationIn ?? {
|
|
2144
|
+
icon: "builtin:openai",
|
|
2145
|
+
label: name
|
|
2146
|
+
};
|
|
2147
|
+
}
|
|
2148
|
+
getCredentialRequirements() {
|
|
2149
|
+
return [{
|
|
2150
|
+
slotKey: this.credentialSlotKey,
|
|
2151
|
+
label: "OpenAI API key",
|
|
2152
|
+
acceptedTypes: ["openai.apiKey"]
|
|
2153
|
+
}];
|
|
2154
|
+
}
|
|
2155
|
+
};
|
|
2156
|
+
|
|
2157
|
+
//#endregion
|
|
2158
|
+
//#region src/chatModels/OpenAiChatModelPresetsFactory.ts
|
|
2159
|
+
/**
|
|
2160
|
+
* Default OpenAI chat model configs for scaffolds and demos (icon + label match {@link OpenAIChatModelConfig} defaults).
|
|
2161
|
+
* Prefer importing {@link openAiChatModelPresets} from here or from the consumer template re-export
|
|
2162
|
+
* instead of repeating {@link OpenAIChatModelConfig} construction in app workflows.
|
|
2163
|
+
*/
|
|
2164
|
+
var OpenAiChatModelPresets = class {
|
|
2165
|
+
demoGpt4oMini = new OpenAIChatModelConfig("OpenAI", "gpt-4o-mini");
|
|
2166
|
+
demoGpt41 = new OpenAIChatModelConfig("OpenAI", "gpt-4.1");
|
|
2167
|
+
};
|
|
2168
|
+
const openAiChatModelPresets = new OpenAiChatModelPresets();
|
|
2169
|
+
|
|
2170
|
+
//#endregion
|
|
2171
|
+
//#region src/nodes/AgentMessageFactory.ts
|
|
2172
|
+
/**
|
|
2173
|
+
* AI-SDK-shaped message construction for the AIAgent stack. Emits plain `ModelMessage[]`
|
|
2174
|
+
* ( `{ role: 'system' | 'user' | 'assistant' | 'tool', content: ... }` ) as consumed by
|
|
2175
|
+
* `generateText({ messages })` from the `ai` package.
|
|
2176
|
+
*/
|
|
2177
|
+
var AgentMessageFactory = class AgentMessageFactory {
|
|
2178
|
+
static createPromptMessages(messages) {
|
|
2179
|
+
return messages.map((message) => this.createPromptMessage(message));
|
|
2180
|
+
}
|
|
2181
|
+
/**
|
|
2182
|
+
* Builds the assistant message that contains optional text plus one or more tool-call parts,
|
|
2183
|
+
* matching the shape AI SDK emits between steps.
|
|
2184
|
+
*/
|
|
2185
|
+
static createAssistantWithToolCalls(text, toolCalls) {
|
|
2186
|
+
const content = [];
|
|
2187
|
+
if (text && text.length > 0) content.push({
|
|
2188
|
+
type: "text",
|
|
2189
|
+
text
|
|
2190
|
+
});
|
|
2191
|
+
for (const toolCall of toolCalls) content.push({
|
|
2192
|
+
type: "tool-call",
|
|
2193
|
+
toolCallId: toolCall.id ?? toolCall.name,
|
|
2194
|
+
toolName: toolCall.name,
|
|
2195
|
+
input: toolCall.input ?? {}
|
|
2196
|
+
});
|
|
2197
|
+
return {
|
|
2198
|
+
role: "assistant",
|
|
2199
|
+
content
|
|
2200
|
+
};
|
|
2201
|
+
}
|
|
2202
|
+
/**
|
|
2203
|
+
* Builds the `{ role: "tool", content: [{ type: "tool-result", ... }, ...] }` message returned
|
|
2204
|
+
* to the model after each tool round.
|
|
2205
|
+
*/
|
|
2206
|
+
static createToolResultsMessage(executedToolCalls) {
|
|
2207
|
+
return {
|
|
2208
|
+
role: "tool",
|
|
2209
|
+
content: executedToolCalls.map((executed) => ({
|
|
2210
|
+
type: "tool-result",
|
|
2211
|
+
toolCallId: executed.toolCallId,
|
|
2212
|
+
toolName: executed.toolName,
|
|
2213
|
+
output: {
|
|
2214
|
+
type: "json",
|
|
2215
|
+
value: AgentMessageFactory.toToolResultJson(executed.result)
|
|
2216
|
+
}
|
|
2217
|
+
}))
|
|
2218
|
+
};
|
|
2219
|
+
}
|
|
2220
|
+
static toToolResultJson(value) {
|
|
2221
|
+
if (value === void 0) return null;
|
|
2222
|
+
try {
|
|
2223
|
+
return JSON.parse(JSON.stringify(value));
|
|
2224
|
+
} catch {
|
|
2225
|
+
return String(value);
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
static createPromptMessage(message) {
|
|
2229
|
+
if (message.role === "system") return {
|
|
2230
|
+
role: "system",
|
|
2231
|
+
content: message.content
|
|
2232
|
+
};
|
|
2233
|
+
if (message.role === "assistant") return {
|
|
2234
|
+
role: "assistant",
|
|
2235
|
+
content: message.content
|
|
2236
|
+
};
|
|
2237
|
+
return {
|
|
2238
|
+
role: "user",
|
|
2239
|
+
content: message.content
|
|
2240
|
+
};
|
|
2241
|
+
}
|
|
2242
|
+
};
|
|
2243
|
+
|
|
2244
|
+
//#endregion
|
|
2245
|
+
//#region src/nodes/AgentOutputFactory.ts
|
|
2246
|
+
var AgentOutputFactory = class {
|
|
2247
|
+
static fromUnknown(value) {
|
|
2248
|
+
return { main: [{ json: value }] };
|
|
2249
|
+
}
|
|
2250
|
+
static replaceJson(item, value) {
|
|
2251
|
+
return {
|
|
2252
|
+
...item,
|
|
2253
|
+
json: value
|
|
2254
|
+
};
|
|
2255
|
+
}
|
|
2256
|
+
static fromAgentContent(content) {
|
|
2257
|
+
try {
|
|
2258
|
+
return JSON.parse(content);
|
|
2259
|
+
} catch {
|
|
2260
|
+
return { output: content };
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
};
|
|
2264
|
+
|
|
2226
2265
|
//#endregion
|
|
2227
2266
|
//#region src/nodes/AgentStructuredOutputRepairPromptFactory.ts
|
|
2228
2267
|
var _ref$4, _AgentStructuredOutputRepairPromptFactory;
|
|
@@ -2601,31 +2640,26 @@ let AgentStructuredOutputRunner = class AgentStructuredOutputRunner$1 {
|
|
|
2601
2640
|
_AgentStructuredOutputRunner = this;
|
|
2602
2641
|
}
|
|
2603
2642
|
static repairAttemptCount = 2;
|
|
2604
|
-
|
|
2643
|
+
static structuredOutputSchemaName = "agent_output";
|
|
2644
|
+
constructor(repairPromptFactory, openAiStrictJsonSchemaFactory) {
|
|
2605
2645
|
this.repairPromptFactory = repairPromptFactory;
|
|
2606
|
-
this.
|
|
2646
|
+
this.openAiStrictJsonSchemaFactory = openAiStrictJsonSchemaFactory;
|
|
2607
2647
|
}
|
|
2608
2648
|
async resolve(args) {
|
|
2609
2649
|
let lastFailure;
|
|
2610
|
-
if (args.
|
|
2611
|
-
const directResult = this.tryParseAndValidate(
|
|
2612
|
-
if (directResult.ok) return directResult.value;
|
|
2613
|
-
lastFailure = directResult;
|
|
2614
|
-
} else if (!this.supportsNativeStructuredOutput(args.model)) {
|
|
2615
|
-
const rawResponse = await args.invokeTextModel(args.conversation);
|
|
2616
|
-
const directResult = this.tryParseAndValidate(AgentMessageFactory.extractContent(rawResponse), args.schema);
|
|
2650
|
+
if (args.rawFinalText !== void 0) {
|
|
2651
|
+
const directResult = this.tryParseAndValidate(args.rawFinalText, args.schema);
|
|
2617
2652
|
if (directResult.ok) return directResult.value;
|
|
2618
2653
|
lastFailure = directResult;
|
|
2619
2654
|
}
|
|
2620
2655
|
try {
|
|
2621
|
-
const
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
}
|
|
2656
|
+
const structuredOptions = this.resolveStructuredOutputOptions(args.chatModelConfig);
|
|
2657
|
+
const schemaForModel = this.resolveOutputSchemaForModel(args.schema, structuredOptions);
|
|
2658
|
+
const nativeResult = this.tryValidateStructuredValue(await args.invokeStructuredModel(schemaForModel, args.conversation, structuredOptions), args.schema);
|
|
2659
|
+
if (nativeResult.ok) return nativeResult.value;
|
|
2660
|
+
lastFailure = nativeResult;
|
|
2627
2661
|
} catch (error) {
|
|
2628
|
-
lastFailure = {
|
|
2662
|
+
lastFailure = lastFailure ?? {
|
|
2629
2663
|
ok: false,
|
|
2630
2664
|
invalidContent: "",
|
|
2631
2665
|
validationError: `Native structured output failed: ${this.summarizeError(error)}`
|
|
@@ -2649,22 +2683,26 @@ let AgentStructuredOutputRunner = class AgentStructuredOutputRunner$1 {
|
|
|
2649
2683
|
validationError: failure.validationError
|
|
2650
2684
|
}))];
|
|
2651
2685
|
const repairResponse = await args.invokeTextModel(repairMessages);
|
|
2652
|
-
const repairResult = this.tryParseAndValidate(
|
|
2686
|
+
const repairResult = this.tryParseAndValidate(repairResponse.text, args.schema);
|
|
2653
2687
|
if (repairResult.ok) return repairResult.value;
|
|
2654
2688
|
failure = repairResult;
|
|
2655
2689
|
}
|
|
2656
2690
|
throw new Error(`Structured output required for AIAgent "${args.agentName}" (${args.nodeId}) but validation still failed after ${_AgentStructuredOutputRunner.repairAttemptCount} repair attempts: ${failure.validationError}`);
|
|
2657
2691
|
}
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
return
|
|
2692
|
+
/**
|
|
2693
|
+
* Chooses strict mode for OpenAI chat-model configs, off otherwise. Extendable in future for
|
|
2694
|
+
* other providers that adopt the same "supply a JSON Schema record directly" contract.
|
|
2695
|
+
*/
|
|
2696
|
+
resolveStructuredOutputOptions(chatModelConfig) {
|
|
2697
|
+
if (chatModelConfig.type !== OpenAIChatModelFactory) return;
|
|
2698
|
+
return {
|
|
2699
|
+
strict: true,
|
|
2700
|
+
schemaName: _AgentStructuredOutputRunner.structuredOutputSchemaName
|
|
2701
|
+
};
|
|
2665
2702
|
}
|
|
2666
|
-
|
|
2667
|
-
|
|
2703
|
+
resolveOutputSchemaForModel(schema, options) {
|
|
2704
|
+
if (!options?.strict) return schema;
|
|
2705
|
+
return this.openAiStrictJsonSchemaFactory.createStructuredOutputRecord(schema, { schemaName: options.schemaName ?? _AgentStructuredOutputRunner.structuredOutputSchemaName });
|
|
2668
2706
|
}
|
|
2669
2707
|
tryParseAndValidate(content, schema) {
|
|
2670
2708
|
try {
|
|
@@ -2698,7 +2736,7 @@ let AgentStructuredOutputRunner = class AgentStructuredOutputRunner$1 {
|
|
|
2698
2736
|
}
|
|
2699
2737
|
toJson(value) {
|
|
2700
2738
|
try {
|
|
2701
|
-
return JSON.stringify(value);
|
|
2739
|
+
return JSON.stringify(value) ?? String(value);
|
|
2702
2740
|
} catch (error) {
|
|
2703
2741
|
return `<<unserializable: ${this.summarizeError(error)}>>`;
|
|
2704
2742
|
}
|
|
@@ -2707,8 +2745,8 @@ let AgentStructuredOutputRunner = class AgentStructuredOutputRunner$1 {
|
|
|
2707
2745
|
AgentStructuredOutputRunner = _AgentStructuredOutputRunner = __decorate([
|
|
2708
2746
|
(0, __codemation_core.injectable)(),
|
|
2709
2747
|
__decorateParam(0, (0, __codemation_core.inject)(AgentStructuredOutputRepairPromptFactory)),
|
|
2710
|
-
__decorateParam(1, (0, __codemation_core.inject)(
|
|
2711
|
-
__decorateMetadata("design:paramtypes", [typeof (_ref$3 = typeof AgentStructuredOutputRepairPromptFactory !== "undefined" && AgentStructuredOutputRepairPromptFactory) === "function" ? _ref$3 : Object, typeof (_ref2$3 = typeof
|
|
2748
|
+
__decorateParam(1, (0, __codemation_core.inject)(OpenAiStrictJsonSchemaFactory)),
|
|
2749
|
+
__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])
|
|
2712
2750
|
], AgentStructuredOutputRunner);
|
|
2713
2751
|
|
|
2714
2752
|
//#endregion
|
|
@@ -2862,8 +2900,8 @@ let AgentToolExecutionCoordinator = class AgentToolExecutionCoordinator$1 {
|
|
|
2862
2900
|
inputsByPort: toolCallInputsByPort
|
|
2863
2901
|
});
|
|
2864
2902
|
try {
|
|
2865
|
-
const
|
|
2866
|
-
const
|
|
2903
|
+
const result = await plannedToolCall.binding.execute(plannedToolCall.toolCall.input ?? {});
|
|
2904
|
+
const serialized = typeof result === "string" ? result : JSON.stringify(result);
|
|
2867
2905
|
const finishedAt = /* @__PURE__ */ new Date();
|
|
2868
2906
|
await ctx.nodeState?.markCompleted({
|
|
2869
2907
|
nodeId: plannedToolCall.nodeId,
|
|
@@ -2907,7 +2945,7 @@ let AgentToolExecutionCoordinator = class AgentToolExecutionCoordinator$1 {
|
|
|
2907
2945
|
const classification = this.errorClassifier.classify({
|
|
2908
2946
|
error,
|
|
2909
2947
|
toolName: plannedToolCall.binding.config.name,
|
|
2910
|
-
schema: plannedToolCall.binding.
|
|
2948
|
+
schema: plannedToolCall.binding.inputSchema
|
|
2911
2949
|
});
|
|
2912
2950
|
if (classification.kind !== "repairable_validation_error") {
|
|
2913
2951
|
const effectiveError = classification.effectiveError;
|
|
@@ -3070,14 +3108,6 @@ let AgentToolExecutionCoordinator = class AgentToolExecutionCoordinator$1 {
|
|
|
3070
3108
|
if (!firstIssue) return `Your previous tool call for "${toolName}" was invalid and did not match the expected schema.`;
|
|
3071
3109
|
return `Your previous tool call for "${toolName}" was invalid because field "${firstIssue.path.length > 0 ? firstIssue.path.join(".") : "<root>"}" failed validation: ${firstIssue.message}`;
|
|
3072
3110
|
}
|
|
3073
|
-
parseToolOutput(serialized) {
|
|
3074
|
-
if (typeof serialized !== "string") return serialized;
|
|
3075
|
-
try {
|
|
3076
|
-
return JSON.parse(serialized);
|
|
3077
|
-
} catch {
|
|
3078
|
-
return serialized;
|
|
3079
|
-
}
|
|
3080
|
-
}
|
|
3081
3111
|
toJsonValue(value) {
|
|
3082
3112
|
if (value === void 0) return;
|
|
3083
3113
|
return JSON.parse(JSON.stringify(value));
|
|
@@ -3197,14 +3227,8 @@ var _ref, _ref2, _ref3, _ref4;
|
|
|
3197
3227
|
let AIAgentNode = class AIAgentNode$1 {
|
|
3198
3228
|
kind = "node";
|
|
3199
3229
|
outputPorts = ["main"];
|
|
3200
|
-
/**
|
|
3201
|
-
* Engine validates {@link RunnableNodeConfig.inputSchema} (Zod) on {@code item.json} before enqueue, then resolves
|
|
3202
|
-
* per-item **`itemExpr`** leaves on config before {@link #execute}. Prefer modeling prompts as
|
|
3203
|
-
* {@code { messages: [{ role, content }, ...] }} (on input or config) so persisted inputs are visible in the UI.
|
|
3204
|
-
*/
|
|
3205
3230
|
inputSchema = unknown();
|
|
3206
3231
|
connectionCredentialExecutionContextFactory;
|
|
3207
|
-
/** One resolved model/tools bundle per activation context (same ctx across items in a batch). */
|
|
3208
3232
|
preparedByExecutionContext = /* @__PURE__ */ new WeakMap();
|
|
3209
3233
|
constructor(nodeResolver, credentialSessions, nodeBackedToolRuntime, executionHelpers, structuredOutputRunner, toolExecutionCoordinator) {
|
|
3210
3234
|
this.nodeResolver = nodeResolver;
|
|
@@ -3235,9 +3259,6 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3235
3259
|
throw error;
|
|
3236
3260
|
}
|
|
3237
3261
|
}
|
|
3238
|
-
/**
|
|
3239
|
-
* Resolves the chat model and tools once per activation, then reuses for every item in the batch.
|
|
3240
|
-
*/
|
|
3241
3262
|
async prepareExecution(ctx) {
|
|
3242
3263
|
const chatModelFactory = this.nodeResolver.resolve(ctx.config.chatModel.type);
|
|
3243
3264
|
const languageModelCredentialContext = this.connectionCredentialExecutionContextFactory.forConnectionNode(ctx, {
|
|
@@ -3255,9 +3276,6 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3255
3276
|
languageModelConnectionNodeId: __codemation_core.ConnectionNodeIdFactory.languageModelConnectionNodeId(ctx.nodeId)
|
|
3256
3277
|
};
|
|
3257
3278
|
}
|
|
3258
|
-
/**
|
|
3259
|
-
* One item: build prompts, optionally bind tools, run the multi-turn loop, map the final model message to workflow JSON.
|
|
3260
|
-
*/
|
|
3261
3279
|
async runAgentForItem(prepared, item, itemIndex, items) {
|
|
3262
3280
|
const { ctx } = prepared;
|
|
3263
3281
|
const itemInputsByPort = AgentItemPortMap.fromItem(item);
|
|
@@ -3271,8 +3289,8 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3271
3289
|
conversation,
|
|
3272
3290
|
agentName: this.getAgentDisplayName(ctx),
|
|
3273
3291
|
nodeId: ctx.nodeId,
|
|
3274
|
-
invokeTextModel: async (messages) => await this.
|
|
3275
|
-
invokeStructuredModel: async (
|
|
3292
|
+
invokeTextModel: async (messages) => await this.invokeTextTurn(prepared, itemInputsByPort, messages, []),
|
|
3293
|
+
invokeStructuredModel: async (schema, messages, structuredOptions) => await this.invokeStructuredTurn(prepared, itemInputsByPort, schema, messages, structuredOptions)
|
|
3276
3294
|
});
|
|
3277
3295
|
await ctx.telemetry.recordMetric({
|
|
3278
3296
|
name: __codemation_core.CodemationTelemetryMetricNames.agentTurns,
|
|
@@ -3284,13 +3302,11 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3284
3302
|
});
|
|
3285
3303
|
return this.buildOutputItem(item, structuredOutput);
|
|
3286
3304
|
}
|
|
3287
|
-
const modelWithTools = this.bindToolsToModel(prepared.model, itemScopedTools);
|
|
3288
3305
|
const loopResult = await this.runTurnLoopUntilFinalAnswer({
|
|
3289
3306
|
prepared,
|
|
3290
3307
|
itemInputsByPort,
|
|
3291
3308
|
itemScopedTools,
|
|
3292
|
-
conversation
|
|
3293
|
-
modelWithTools
|
|
3309
|
+
conversation
|
|
3294
3310
|
});
|
|
3295
3311
|
await ctx.telemetry.recordMetric({
|
|
3296
3312
|
name: __codemation_core.CodemationTelemetryMetricNames.agentTurns,
|
|
@@ -3300,30 +3316,34 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3300
3316
|
name: __codemation_core.CodemationTelemetryMetricNames.agentToolCalls,
|
|
3301
3317
|
value: loopResult.toolCallCount
|
|
3302
3318
|
});
|
|
3303
|
-
const outputJson = await this.resolveFinalOutputJson(prepared, itemInputsByPort, conversation, loopResult.
|
|
3319
|
+
const outputJson = await this.resolveFinalOutputJson(prepared, itemInputsByPort, conversation, loopResult.finalText, itemScopedTools.length > 0);
|
|
3304
3320
|
return this.buildOutputItem(item, outputJson);
|
|
3305
3321
|
}
|
|
3306
3322
|
/**
|
|
3307
|
-
*
|
|
3323
|
+
* Multi-turn loop:
|
|
3324
|
+
* - Each turn is a single `generateText` call with tools exposed but **not auto-executed**
|
|
3325
|
+
* (we control tool dispatch so that {@link AgentToolExecutionCoordinator} drives repair /
|
|
3326
|
+
* connection-invocation recording / transient-error handling exactly like before).
|
|
3327
|
+
* - When the model returns no tool calls the loop ends with the model's text as the final answer.
|
|
3328
|
+
* - Respects `guardrails.maxTurns` and `guardrails.onTurnLimitReached`.
|
|
3308
3329
|
*/
|
|
3309
3330
|
async runTurnLoopUntilFinalAnswer(args) {
|
|
3310
|
-
const { prepared, itemInputsByPort, itemScopedTools, conversation
|
|
3311
|
-
const { ctx, guardrails
|
|
3312
|
-
let
|
|
3331
|
+
const { prepared, itemInputsByPort, itemScopedTools, conversation } = args;
|
|
3332
|
+
const { ctx, guardrails } = prepared;
|
|
3333
|
+
let finalText = "";
|
|
3313
3334
|
let toolCallCount = 0;
|
|
3314
3335
|
let turnCount = 0;
|
|
3315
3336
|
const repairAttemptsByToolName = /* @__PURE__ */ new Map();
|
|
3316
3337
|
for (let turn = 1; turn <= guardrails.maxTurns; turn++) {
|
|
3317
3338
|
turnCount = turn;
|
|
3318
|
-
const
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
if (toolCalls.length === 0) break;
|
|
3339
|
+
const result = await this.invokeTextTurn(prepared, itemInputsByPort, conversation, itemScopedTools);
|
|
3340
|
+
finalText = result.text;
|
|
3341
|
+
if (result.toolCalls.length === 0) break;
|
|
3322
3342
|
if (this.cannotExecuteAnotherToolRound(turn, guardrails)) {
|
|
3323
3343
|
this.finishOrThrowWhenTurnCapHitWithToolCalls(ctx, guardrails);
|
|
3324
3344
|
break;
|
|
3325
3345
|
}
|
|
3326
|
-
const plannedToolCalls = this.planToolCalls(itemScopedTools, toolCalls, ctx.nodeId);
|
|
3346
|
+
const plannedToolCalls = this.planToolCalls(itemScopedTools, result.toolCalls, ctx.nodeId);
|
|
3327
3347
|
toolCallCount += plannedToolCalls.length;
|
|
3328
3348
|
await this.markQueuedTools(plannedToolCalls, ctx);
|
|
3329
3349
|
const executedToolCalls = await this.toolExecutionCoordinator.execute({
|
|
@@ -3332,11 +3352,10 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3332
3352
|
agentName: this.getAgentDisplayName(ctx),
|
|
3333
3353
|
repairAttemptsByToolName
|
|
3334
3354
|
});
|
|
3335
|
-
this.appendAssistantAndToolMessages(conversation,
|
|
3355
|
+
this.appendAssistantAndToolMessages(conversation, result.assistantMessage, result.text, result.toolCalls, executedToolCalls);
|
|
3336
3356
|
}
|
|
3337
|
-
if (!finalResponse) throw new Error(`AIAgent "${ctx.config.name ?? ctx.nodeId}" did not produce a model response.`);
|
|
3338
3357
|
return {
|
|
3339
|
-
|
|
3358
|
+
finalText,
|
|
3340
3359
|
turnCount,
|
|
3341
3360
|
toolCallCount
|
|
3342
3361
|
};
|
|
@@ -3348,30 +3367,30 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3348
3367
|
if (guardrails.onTurnLimitReached === "respondWithLastMessage") return;
|
|
3349
3368
|
throw new Error(`AIAgent "${ctx.config.name ?? ctx.nodeId}" reached maxTurns=${guardrails.maxTurns} before producing a final response.`);
|
|
3350
3369
|
}
|
|
3351
|
-
appendAssistantAndToolMessages(conversation, assistantMessage, executedToolCalls) {
|
|
3352
|
-
conversation.push(assistantMessage
|
|
3370
|
+
appendAssistantAndToolMessages(conversation, assistantMessage, text, toolCalls, executedToolCalls) {
|
|
3371
|
+
conversation.push(assistantMessage ?? AgentMessageFactory.createAssistantWithToolCalls(text, toolCalls), AgentMessageFactory.createToolResultsMessage(executedToolCalls));
|
|
3353
3372
|
}
|
|
3354
|
-
async resolveFinalOutputJson(prepared, itemInputsByPort, conversation,
|
|
3355
|
-
if (!prepared.ctx.config.outputSchema) return AgentOutputFactory.fromAgentContent(
|
|
3373
|
+
async resolveFinalOutputJson(prepared, itemInputsByPort, conversation, finalText, wasToolEnabledRun) {
|
|
3374
|
+
if (!prepared.ctx.config.outputSchema) return AgentOutputFactory.fromAgentContent(finalText);
|
|
3375
|
+
const conversationWithFinal = wasToolEnabledRun ? [...conversation, {
|
|
3376
|
+
role: "assistant",
|
|
3377
|
+
content: finalText
|
|
3378
|
+
}] : conversation;
|
|
3356
3379
|
return await this.structuredOutputRunner.resolve({
|
|
3357
3380
|
model: prepared.model,
|
|
3358
3381
|
chatModelConfig: prepared.ctx.config.chatModel,
|
|
3359
3382
|
schema: prepared.ctx.config.outputSchema,
|
|
3360
|
-
conversation:
|
|
3361
|
-
|
|
3383
|
+
conversation: conversationWithFinal,
|
|
3384
|
+
rawFinalText: finalText,
|
|
3362
3385
|
agentName: this.getAgentDisplayName(prepared.ctx),
|
|
3363
3386
|
nodeId: prepared.ctx.nodeId,
|
|
3364
|
-
invokeTextModel: async (messages) => await this.
|
|
3365
|
-
invokeStructuredModel: async (
|
|
3387
|
+
invokeTextModel: async (messages) => await this.invokeTextTurn(prepared, itemInputsByPort, messages, []),
|
|
3388
|
+
invokeStructuredModel: async (schema, messages, structuredOptions) => await this.invokeStructuredTurn(prepared, itemInputsByPort, schema, messages, structuredOptions)
|
|
3366
3389
|
});
|
|
3367
3390
|
}
|
|
3368
3391
|
buildOutputItem(item, outputJson) {
|
|
3369
3392
|
return AgentOutputFactory.replaceJson(item, outputJson);
|
|
3370
3393
|
}
|
|
3371
|
-
bindToolsToModel(model, itemScopedTools) {
|
|
3372
|
-
if (itemScopedTools.length === 0 || !model.bindTools) return model;
|
|
3373
|
-
return model.bindTools(itemScopedTools.map((entry) => entry.langChainTool));
|
|
3374
|
-
}
|
|
3375
3394
|
resolveTools(toolConfigs) {
|
|
3376
3395
|
const resolvedTools = toolConfigs.map((config$1) => ({
|
|
3377
3396
|
config: config$1,
|
|
@@ -3390,38 +3409,93 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3390
3409
|
connectionNodeId: __codemation_core.ConnectionNodeIdFactory.toolConnectionNodeId(ctx.nodeId, entry.config.name),
|
|
3391
3410
|
getCredentialRequirements: () => entry.config.getCredentialRequirements?.() ?? []
|
|
3392
3411
|
});
|
|
3393
|
-
const langChainTool = this.executionHelpers.createDynamicStructuredTool(entry, toolCredentialContext, item, itemIndex, items);
|
|
3394
3412
|
return {
|
|
3395
3413
|
config: entry.config,
|
|
3396
|
-
|
|
3414
|
+
inputSchema: entry.runtime.inputSchema,
|
|
3415
|
+
execute: async (input) => {
|
|
3416
|
+
const validated = entry.runtime.inputSchema.parse(input);
|
|
3417
|
+
return await entry.runtime.execute({
|
|
3418
|
+
config: entry.config,
|
|
3419
|
+
input: validated,
|
|
3420
|
+
ctx: toolCredentialContext,
|
|
3421
|
+
item,
|
|
3422
|
+
itemIndex,
|
|
3423
|
+
items
|
|
3424
|
+
});
|
|
3425
|
+
}
|
|
3397
3426
|
};
|
|
3398
3427
|
});
|
|
3399
3428
|
}
|
|
3400
|
-
|
|
3429
|
+
/**
|
|
3430
|
+
* Builds an AI SDK {@link ToolSet} where every tool ships a pre-converted JSON Schema (via
|
|
3431
|
+
* {@link jsonSchema}) — not the raw Zod schema — and carries **no** `execute`. Two reasons:
|
|
3432
|
+
*
|
|
3433
|
+
* 1. Codemation owns tool dispatch + the per-tool repair loop (see {@link AgentToolExecutionCoordinator}),
|
|
3434
|
+
* so the AI SDK must surface tool calls back to us instead of auto-running them.
|
|
3435
|
+
* 2. The AI SDK's `asSchema` helper discriminates between Zod v3 / Zod v4 / Standard Schema via
|
|
3436
|
+
* runtime feature-detection (`~standard`, `_zod`, etc.). Handing it a pre-built
|
|
3437
|
+
* {@link jsonSchema} record — which is tagged with `Symbol.for('vercel.ai.schema')` — skips all
|
|
3438
|
+
* of that detection and guarantees the provider receives a draft-07 JSON Schema with
|
|
3439
|
+
* `additionalProperties: false` at every object depth (see {@link OpenAiStrictJsonSchemaFactory}
|
|
3440
|
+
* for the same logic applied to structured-output schemas). Codemation still runs its own Zod
|
|
3441
|
+
* validation on tool inputs before execute — the schema handed to the model is advisory.
|
|
3442
|
+
*/
|
|
3443
|
+
buildToolSet(itemScopedTools) {
|
|
3444
|
+
if (itemScopedTools.length === 0) return void 0;
|
|
3445
|
+
const toolSet = {};
|
|
3446
|
+
for (const entry of itemScopedTools) {
|
|
3447
|
+
const schemaRecord = this.executionHelpers.createJsonSchemaRecord(entry.inputSchema, {
|
|
3448
|
+
schemaName: entry.config.name,
|
|
3449
|
+
requireObjectRoot: true
|
|
3450
|
+
});
|
|
3451
|
+
toolSet[entry.config.name] = {
|
|
3452
|
+
description: entry.config.description,
|
|
3453
|
+
inputSchema: (0, ai.jsonSchema)(schemaRecord)
|
|
3454
|
+
};
|
|
3455
|
+
}
|
|
3456
|
+
return toolSet;
|
|
3457
|
+
}
|
|
3458
|
+
/**
|
|
3459
|
+
* One `generateText` turn (no auto tool execution) with Codemation-owned child-span telemetry
|
|
3460
|
+
* and connection-invocation state recording.
|
|
3461
|
+
*/
|
|
3462
|
+
async invokeTextTurn(prepared, itemInputsByPort, messages, itemScopedTools) {
|
|
3401
3463
|
const invocationId = __codemation_core.ConnectionInvocationIdFactory.create();
|
|
3402
3464
|
const startedAt = /* @__PURE__ */ new Date();
|
|
3403
3465
|
const summarizedInput = this.summarizeLlmMessages(messages);
|
|
3466
|
+
const { ctx, model, languageModelConnectionNodeId, guardrails } = prepared;
|
|
3404
3467
|
const span = this.createModelInvocationSpan(ctx, invocationId, startedAt);
|
|
3405
3468
|
await ctx.nodeState?.markQueued({
|
|
3406
|
-
nodeId,
|
|
3469
|
+
nodeId: languageModelConnectionNodeId,
|
|
3407
3470
|
activationId: ctx.activationId,
|
|
3408
|
-
inputsByPort
|
|
3471
|
+
inputsByPort: itemInputsByPort
|
|
3409
3472
|
});
|
|
3410
3473
|
await ctx.nodeState?.markRunning({
|
|
3411
|
-
nodeId,
|
|
3474
|
+
nodeId: languageModelConnectionNodeId,
|
|
3412
3475
|
activationId: ctx.activationId,
|
|
3413
|
-
inputsByPort
|
|
3476
|
+
inputsByPort: itemInputsByPort
|
|
3414
3477
|
});
|
|
3415
3478
|
try {
|
|
3416
|
-
const
|
|
3479
|
+
const tools = this.buildToolSet(itemScopedTools);
|
|
3480
|
+
const callOptions = this.resolveCallOptions(model, guardrails.modelInvocationOptions);
|
|
3481
|
+
const result = await (0, ai.generateText)({
|
|
3482
|
+
model: model.languageModel,
|
|
3483
|
+
messages: [...messages],
|
|
3484
|
+
tools,
|
|
3485
|
+
toolChoice: tools ? "auto" : void 0,
|
|
3486
|
+
maxOutputTokens: callOptions.maxOutputTokens,
|
|
3487
|
+
temperature: callOptions.temperature,
|
|
3488
|
+
providerOptions: callOptions.providerOptions,
|
|
3489
|
+
maxRetries: 0
|
|
3490
|
+
});
|
|
3491
|
+
const turnResult = this.extractTurnResult(result);
|
|
3417
3492
|
const finishedAt = /* @__PURE__ */ new Date();
|
|
3418
3493
|
await ctx.nodeState?.markCompleted({
|
|
3419
|
-
nodeId,
|
|
3494
|
+
nodeId: languageModelConnectionNodeId,
|
|
3420
3495
|
activationId: ctx.activationId,
|
|
3421
|
-
inputsByPort,
|
|
3422
|
-
outputs: AgentOutputFactory.fromUnknown({ content:
|
|
3496
|
+
inputsByPort: itemInputsByPort,
|
|
3497
|
+
outputs: AgentOutputFactory.fromUnknown({ content: turnResult.text })
|
|
3423
3498
|
});
|
|
3424
|
-
const content = AgentMessageFactory.extractContent(response);
|
|
3425
3499
|
await span.attachArtifact({
|
|
3426
3500
|
kind: "ai.messages",
|
|
3427
3501
|
contentType: "application/json",
|
|
@@ -3430,26 +3504,26 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3430
3504
|
await span.attachArtifact({
|
|
3431
3505
|
kind: "ai.response",
|
|
3432
3506
|
contentType: "application/json",
|
|
3433
|
-
previewJson:
|
|
3507
|
+
previewJson: turnResult.text
|
|
3434
3508
|
});
|
|
3435
|
-
await this.recordModelUsageMetrics(span,
|
|
3509
|
+
await this.recordModelUsageMetrics(span, turnResult.usage, ctx);
|
|
3436
3510
|
await span.end({
|
|
3437
3511
|
status: "ok",
|
|
3438
3512
|
endedAt: finishedAt
|
|
3439
3513
|
});
|
|
3440
3514
|
await ctx.nodeState?.appendConnectionInvocation({
|
|
3441
3515
|
invocationId,
|
|
3442
|
-
connectionNodeId:
|
|
3516
|
+
connectionNodeId: languageModelConnectionNodeId,
|
|
3443
3517
|
parentAgentNodeId: ctx.nodeId,
|
|
3444
3518
|
parentAgentActivationId: ctx.activationId,
|
|
3445
3519
|
status: "completed",
|
|
3446
3520
|
managedInput: summarizedInput,
|
|
3447
|
-
managedOutput:
|
|
3521
|
+
managedOutput: turnResult.text,
|
|
3448
3522
|
queuedAt: startedAt.toISOString(),
|
|
3449
3523
|
startedAt: startedAt.toISOString(),
|
|
3450
3524
|
finishedAt: finishedAt.toISOString()
|
|
3451
3525
|
});
|
|
3452
|
-
return
|
|
3526
|
+
return turnResult;
|
|
3453
3527
|
} catch (error) {
|
|
3454
3528
|
await span.end({
|
|
3455
3529
|
status: "error",
|
|
@@ -3460,36 +3534,53 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3460
3534
|
error,
|
|
3461
3535
|
invocationId,
|
|
3462
3536
|
startedAt,
|
|
3463
|
-
nodeId,
|
|
3537
|
+
nodeId: languageModelConnectionNodeId,
|
|
3464
3538
|
ctx,
|
|
3465
|
-
inputsByPort,
|
|
3466
|
-
managedInput:
|
|
3539
|
+
inputsByPort: itemInputsByPort,
|
|
3540
|
+
managedInput: summarizedInput
|
|
3467
3541
|
});
|
|
3468
3542
|
}
|
|
3469
3543
|
}
|
|
3470
|
-
|
|
3544
|
+
/**
|
|
3545
|
+
* Structured-output turn: runs `generateText({ output: Output.object({ schema }) })` via the
|
|
3546
|
+
* structured-output runner. We keep this as a separate helper because the runner needs the raw
|
|
3547
|
+
* validated value (not just text) back, and must be able to retry on Zod failures.
|
|
3548
|
+
*/
|
|
3549
|
+
async invokeStructuredTurn(prepared, itemInputsByPort, schema, messages, structuredOptions) {
|
|
3471
3550
|
const invocationId = __codemation_core.ConnectionInvocationIdFactory.create();
|
|
3472
3551
|
const startedAt = /* @__PURE__ */ new Date();
|
|
3473
3552
|
const summarizedInput = this.summarizeLlmMessages(messages);
|
|
3553
|
+
const { ctx, model, languageModelConnectionNodeId, guardrails } = prepared;
|
|
3474
3554
|
const span = this.createModelInvocationSpan(ctx, invocationId, startedAt);
|
|
3475
3555
|
await ctx.nodeState?.markQueued({
|
|
3476
|
-
nodeId,
|
|
3556
|
+
nodeId: languageModelConnectionNodeId,
|
|
3477
3557
|
activationId: ctx.activationId,
|
|
3478
|
-
inputsByPort
|
|
3558
|
+
inputsByPort: itemInputsByPort
|
|
3479
3559
|
});
|
|
3480
3560
|
await ctx.nodeState?.markRunning({
|
|
3481
|
-
nodeId,
|
|
3561
|
+
nodeId: languageModelConnectionNodeId,
|
|
3482
3562
|
activationId: ctx.activationId,
|
|
3483
|
-
inputsByPort
|
|
3563
|
+
inputsByPort: itemInputsByPort
|
|
3484
3564
|
});
|
|
3485
3565
|
try {
|
|
3486
|
-
const
|
|
3566
|
+
const callOptions = this.resolveCallOptions(model, guardrails.modelInvocationOptions);
|
|
3567
|
+
const outputSchema = structuredOptions?.strict && !this.isZodSchema(schema) ? ai.Output.object({ schema: (0, ai.jsonSchema)(schema) }) : ai.Output.object({ schema });
|
|
3568
|
+
const result = await (0, ai.generateText)({
|
|
3569
|
+
model: model.languageModel,
|
|
3570
|
+
messages: [...messages],
|
|
3571
|
+
experimental_output: outputSchema,
|
|
3572
|
+
maxOutputTokens: callOptions.maxOutputTokens,
|
|
3573
|
+
temperature: callOptions.temperature,
|
|
3574
|
+
providerOptions: callOptions.providerOptions,
|
|
3575
|
+
maxRetries: 0
|
|
3576
|
+
});
|
|
3577
|
+
const turnResult = this.extractTurnResult(result);
|
|
3487
3578
|
const finishedAt = /* @__PURE__ */ new Date();
|
|
3488
3579
|
await ctx.nodeState?.markCompleted({
|
|
3489
|
-
nodeId,
|
|
3580
|
+
nodeId: languageModelConnectionNodeId,
|
|
3490
3581
|
activationId: ctx.activationId,
|
|
3491
|
-
inputsByPort,
|
|
3492
|
-
outputs: AgentOutputFactory.fromUnknown(
|
|
3582
|
+
inputsByPort: itemInputsByPort,
|
|
3583
|
+
outputs: AgentOutputFactory.fromUnknown(result.experimental_output)
|
|
3493
3584
|
});
|
|
3494
3585
|
await span.attachArtifact({
|
|
3495
3586
|
kind: "ai.messages",
|
|
@@ -3499,26 +3590,26 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3499
3590
|
await span.attachArtifact({
|
|
3500
3591
|
kind: "ai.response.structured",
|
|
3501
3592
|
contentType: "application/json",
|
|
3502
|
-
previewJson: this.resultToJsonValue(
|
|
3593
|
+
previewJson: this.resultToJsonValue(result.experimental_output)
|
|
3503
3594
|
});
|
|
3504
|
-
await this.recordModelUsageMetrics(span,
|
|
3595
|
+
await this.recordModelUsageMetrics(span, turnResult.usage, ctx);
|
|
3505
3596
|
await span.end({
|
|
3506
3597
|
status: "ok",
|
|
3507
3598
|
endedAt: finishedAt
|
|
3508
3599
|
});
|
|
3509
3600
|
await ctx.nodeState?.appendConnectionInvocation({
|
|
3510
3601
|
invocationId,
|
|
3511
|
-
connectionNodeId:
|
|
3602
|
+
connectionNodeId: languageModelConnectionNodeId,
|
|
3512
3603
|
parentAgentNodeId: ctx.nodeId,
|
|
3513
3604
|
parentAgentActivationId: ctx.activationId,
|
|
3514
3605
|
status: "completed",
|
|
3515
3606
|
managedInput: summarizedInput,
|
|
3516
|
-
managedOutput: this.resultToJsonValue(
|
|
3607
|
+
managedOutput: this.resultToJsonValue(result.experimental_output),
|
|
3517
3608
|
queuedAt: startedAt.toISOString(),
|
|
3518
3609
|
startedAt: startedAt.toISOString(),
|
|
3519
3610
|
finishedAt: finishedAt.toISOString()
|
|
3520
3611
|
});
|
|
3521
|
-
return
|
|
3612
|
+
return result.experimental_output;
|
|
3522
3613
|
} catch (error) {
|
|
3523
3614
|
await span.end({
|
|
3524
3615
|
status: "error",
|
|
@@ -3529,13 +3620,59 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3529
3620
|
error,
|
|
3530
3621
|
invocationId,
|
|
3531
3622
|
startedAt,
|
|
3532
|
-
nodeId,
|
|
3623
|
+
nodeId: languageModelConnectionNodeId,
|
|
3533
3624
|
ctx,
|
|
3534
|
-
inputsByPort,
|
|
3535
|
-
managedInput:
|
|
3625
|
+
inputsByPort: itemInputsByPort,
|
|
3626
|
+
managedInput: summarizedInput
|
|
3536
3627
|
});
|
|
3537
3628
|
}
|
|
3538
3629
|
}
|
|
3630
|
+
isZodSchema(schema) {
|
|
3631
|
+
return typeof schema.parse === "function";
|
|
3632
|
+
}
|
|
3633
|
+
resolveCallOptions(model, overrides) {
|
|
3634
|
+
const defaults = model.defaultCallOptions ?? {};
|
|
3635
|
+
return {
|
|
3636
|
+
maxOutputTokens: overrides?.maxTokens ?? defaults.maxOutputTokens,
|
|
3637
|
+
temperature: defaults.temperature,
|
|
3638
|
+
providerOptions: overrides?.providerOptions ?? defaults.providerOptions
|
|
3639
|
+
};
|
|
3640
|
+
}
|
|
3641
|
+
extractTurnResult(result) {
|
|
3642
|
+
const usage = this.extractUsageFromResult(result);
|
|
3643
|
+
const text = result.text;
|
|
3644
|
+
const toolCalls = result.toolCalls.map((toolCall) => ({
|
|
3645
|
+
id: toolCall.toolCallId,
|
|
3646
|
+
name: toolCall.toolName,
|
|
3647
|
+
input: toolCall.input
|
|
3648
|
+
}));
|
|
3649
|
+
return {
|
|
3650
|
+
assistantMessage: this.extractAssistantMessage(result),
|
|
3651
|
+
text,
|
|
3652
|
+
toolCalls,
|
|
3653
|
+
usage
|
|
3654
|
+
};
|
|
3655
|
+
}
|
|
3656
|
+
extractAssistantMessage(result) {
|
|
3657
|
+
const assistantMessages = result.response.messages.filter((m) => m.role === "assistant");
|
|
3658
|
+
return assistantMessages[assistantMessages.length - 1];
|
|
3659
|
+
}
|
|
3660
|
+
extractUsageFromResult(result) {
|
|
3661
|
+
const usage = result.usage;
|
|
3662
|
+
const inputTokens = this.toFiniteNumber(usage.inputTokens);
|
|
3663
|
+
const outputTokens = this.toFiniteNumber(usage.outputTokens);
|
|
3664
|
+
return {
|
|
3665
|
+
inputTokens,
|
|
3666
|
+
outputTokens,
|
|
3667
|
+
totalTokens: this.toFiniteNumber(usage.totalTokens) ?? (inputTokens !== void 0 && outputTokens !== void 0 ? inputTokens + outputTokens : void 0),
|
|
3668
|
+
cachedInputTokens: this.toFiniteNumber(usage.cachedInputTokens),
|
|
3669
|
+
reasoningTokens: this.toFiniteNumber(usage.reasoningTokens)
|
|
3670
|
+
};
|
|
3671
|
+
}
|
|
3672
|
+
toFiniteNumber(value) {
|
|
3673
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return void 0;
|
|
3674
|
+
return value;
|
|
3675
|
+
}
|
|
3539
3676
|
createModelInvocationSpan(ctx, invocationId, startedAt) {
|
|
3540
3677
|
return ctx.telemetry.startChildSpan({
|
|
3541
3678
|
name: "gen_ai.chat.completion",
|
|
@@ -3548,9 +3685,15 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3548
3685
|
}
|
|
3549
3686
|
});
|
|
3550
3687
|
}
|
|
3551
|
-
async recordModelUsageMetrics(span,
|
|
3552
|
-
const
|
|
3553
|
-
|
|
3688
|
+
async recordModelUsageMetrics(span, usage, ctx) {
|
|
3689
|
+
const entries = [
|
|
3690
|
+
[__codemation_core.GenAiTelemetryAttributeNames.usageInputTokens, usage.inputTokens],
|
|
3691
|
+
[__codemation_core.GenAiTelemetryAttributeNames.usageOutputTokens, usage.outputTokens],
|
|
3692
|
+
[__codemation_core.GenAiTelemetryAttributeNames.usageTotalTokens, usage.totalTokens],
|
|
3693
|
+
[__codemation_core.GenAiTelemetryAttributeNames.usageCacheReadInputTokens, usage.cachedInputTokens],
|
|
3694
|
+
[__codemation_core.GenAiTelemetryAttributeNames.usageReasoningTokens, usage.reasoningTokens]
|
|
3695
|
+
];
|
|
3696
|
+
for (const [name, value] of entries) {
|
|
3554
3697
|
if (value === void 0) continue;
|
|
3555
3698
|
await span.recordMetric({
|
|
3556
3699
|
name,
|
|
@@ -3565,93 +3708,28 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3565
3708
|
const provider = ctx.config.chatModel.provider;
|
|
3566
3709
|
const pricingKey = ctx.config.chatModel.modelName;
|
|
3567
3710
|
if (!provider || !pricingKey) return;
|
|
3568
|
-
|
|
3569
|
-
const outputTokens = usage[__codemation_core.GenAiTelemetryAttributeNames.usageOutputTokens];
|
|
3570
|
-
if (inputTokens !== void 0) await costTracking.captureUsage({
|
|
3711
|
+
if (usage.inputTokens !== void 0) await costTracking.captureUsage({
|
|
3571
3712
|
component: "chat",
|
|
3572
3713
|
provider,
|
|
3573
3714
|
operation: "completion.input",
|
|
3574
3715
|
pricingKey,
|
|
3575
3716
|
usageUnit: "input_tokens",
|
|
3576
|
-
quantity: inputTokens,
|
|
3717
|
+
quantity: usage.inputTokens,
|
|
3577
3718
|
modelName: pricingKey
|
|
3578
3719
|
});
|
|
3579
|
-
if (outputTokens !== void 0) await costTracking.captureUsage({
|
|
3720
|
+
if (usage.outputTokens !== void 0) await costTracking.captureUsage({
|
|
3580
3721
|
component: "chat",
|
|
3581
3722
|
provider,
|
|
3582
3723
|
operation: "completion.output",
|
|
3583
3724
|
pricingKey,
|
|
3584
3725
|
usageUnit: "output_tokens",
|
|
3585
|
-
quantity: outputTokens,
|
|
3726
|
+
quantity: usage.outputTokens,
|
|
3586
3727
|
modelName: pricingKey
|
|
3587
3728
|
});
|
|
3588
3729
|
}
|
|
3589
3730
|
resolveChatModelName(chatModel$1) {
|
|
3590
3731
|
return chatModel$1.modelName ?? chatModel$1.name;
|
|
3591
3732
|
}
|
|
3592
|
-
extractModelUsageMetrics(response) {
|
|
3593
|
-
const usage = this.extractUsageObject(response);
|
|
3594
|
-
const inputTokens = this.readUsageNumber(usage, [
|
|
3595
|
-
"input_tokens",
|
|
3596
|
-
"inputTokens",
|
|
3597
|
-
"prompt_tokens",
|
|
3598
|
-
"promptTokens"
|
|
3599
|
-
]);
|
|
3600
|
-
const outputTokens = this.readUsageNumber(usage, [
|
|
3601
|
-
"output_tokens",
|
|
3602
|
-
"outputTokens",
|
|
3603
|
-
"completion_tokens",
|
|
3604
|
-
"completionTokens"
|
|
3605
|
-
]);
|
|
3606
|
-
const totalTokens = this.readUsageNumber(usage, ["total_tokens", "totalTokens"]) ?? (inputTokens !== void 0 && outputTokens !== void 0 ? inputTokens + outputTokens : void 0);
|
|
3607
|
-
const cachedInputTokens = this.readUsageNumber(usage, [
|
|
3608
|
-
"cache_read_input_tokens",
|
|
3609
|
-
"cacheReadInputTokens",
|
|
3610
|
-
"input_token_details.cached_tokens"
|
|
3611
|
-
]);
|
|
3612
|
-
const reasoningTokens = this.readUsageNumber(usage, [
|
|
3613
|
-
"reasoning_tokens",
|
|
3614
|
-
"reasoningTokens",
|
|
3615
|
-
"output_token_details.reasoning_tokens"
|
|
3616
|
-
]);
|
|
3617
|
-
return {
|
|
3618
|
-
[__codemation_core.GenAiTelemetryAttributeNames.usageInputTokens]: inputTokens,
|
|
3619
|
-
[__codemation_core.GenAiTelemetryAttributeNames.usageOutputTokens]: outputTokens,
|
|
3620
|
-
[__codemation_core.GenAiTelemetryAttributeNames.usageTotalTokens]: totalTokens,
|
|
3621
|
-
[__codemation_core.GenAiTelemetryAttributeNames.usageCacheReadInputTokens]: cachedInputTokens,
|
|
3622
|
-
[__codemation_core.GenAiTelemetryAttributeNames.usageReasoningTokens]: reasoningTokens
|
|
3623
|
-
};
|
|
3624
|
-
}
|
|
3625
|
-
extractUsageObject(response) {
|
|
3626
|
-
if (!this.isRecord(response)) return;
|
|
3627
|
-
const usageMetadata = response["usage_metadata"];
|
|
3628
|
-
if (this.isRecord(usageMetadata)) return usageMetadata;
|
|
3629
|
-
const responseMetadata = response["response_metadata"];
|
|
3630
|
-
if (this.isRecord(responseMetadata)) {
|
|
3631
|
-
const tokenUsage = responseMetadata["tokenUsage"];
|
|
3632
|
-
if (this.isRecord(tokenUsage)) return tokenUsage;
|
|
3633
|
-
const usage = responseMetadata["usage"];
|
|
3634
|
-
if (this.isRecord(usage)) return usage;
|
|
3635
|
-
}
|
|
3636
|
-
}
|
|
3637
|
-
readUsageNumber(source, keys) {
|
|
3638
|
-
for (const key of keys) {
|
|
3639
|
-
const value = this.readNestedUsageValue(source, key);
|
|
3640
|
-
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
3641
|
-
}
|
|
3642
|
-
}
|
|
3643
|
-
readNestedUsageValue(source, dottedKey) {
|
|
3644
|
-
if (!source) return;
|
|
3645
|
-
let current = source;
|
|
3646
|
-
for (const segment of dottedKey.split(".")) {
|
|
3647
|
-
if (!this.isRecord(current)) return;
|
|
3648
|
-
current = current[segment];
|
|
3649
|
-
}
|
|
3650
|
-
return current;
|
|
3651
|
-
}
|
|
3652
|
-
isRecord(value) {
|
|
3653
|
-
return typeof value === "object" && value !== null;
|
|
3654
|
-
}
|
|
3655
3733
|
async markQueuedTools(plannedToolCalls, ctx) {
|
|
3656
3734
|
for (const plannedToolCall of plannedToolCalls) await ctx.nodeState?.markQueued({
|
|
3657
3735
|
nodeId: plannedToolCall.nodeId,
|
|
@@ -3711,7 +3789,7 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3711
3789
|
};
|
|
3712
3790
|
}
|
|
3713
3791
|
resultToJsonValue(value) {
|
|
3714
|
-
if (value === void 0) return;
|
|
3792
|
+
if (value === void 0) return void 0;
|
|
3715
3793
|
const json = JSON.stringify(value);
|
|
3716
3794
|
return JSON.parse(json);
|
|
3717
3795
|
}
|
|
@@ -3726,7 +3804,7 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3726
3804
|
resolveToolRuntime(config$1) {
|
|
3727
3805
|
if (this.isNodeBackedToolConfig(config$1)) {
|
|
3728
3806
|
const inputSchema = config$1.getInputSchema();
|
|
3729
|
-
if (inputSchema == null) throw new Error(`AIAgent tool "${config$1.name}": node-backed tool is missing inputSchema (cannot build
|
|
3807
|
+
if (inputSchema == null) throw new Error(`AIAgent tool "${config$1.name}": node-backed tool is missing inputSchema (cannot build AI SDK tool).`);
|
|
3730
3808
|
return {
|
|
3731
3809
|
defaultDescription: `Run workflow node "${config$1.node.name ?? config$1.name}" as an AI tool.`,
|
|
3732
3810
|
inputSchema,
|
|
@@ -3735,7 +3813,7 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3735
3813
|
}
|
|
3736
3814
|
if (this.isCallableToolConfig(config$1)) {
|
|
3737
3815
|
const inputSchema = config$1.getInputSchema();
|
|
3738
|
-
if (inputSchema == null) throw new Error(`AIAgent tool "${config$1.name}": callable tool is missing inputSchema (cannot build
|
|
3816
|
+
if (inputSchema == null) throw new Error(`AIAgent tool "${config$1.name}": callable tool is missing inputSchema (cannot build AI SDK tool).`);
|
|
3739
3817
|
return {
|
|
3740
3818
|
defaultDescription: config$1.description ?? `Callable tool "${config$1.name}".`,
|
|
3741
3819
|
inputSchema,
|
|
@@ -3753,17 +3831,9 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
3753
3831
|
execute: async (args) => await Promise.resolve(tool.execute(args))
|
|
3754
3832
|
};
|
|
3755
3833
|
}
|
|
3756
|
-
/**
|
|
3757
|
-
* Consumer apps can resolve two copies of `@codemation/core`, breaking `instanceof NodeBackedToolConfig` and
|
|
3758
|
-
* sending node-backed tools down the plugin-tool branch with `inputSchema: undefined` (LangChain then crashes in
|
|
3759
|
-
* json-schema validation). {@link NodeBackedToolConfig#toolKind} is stable across copies.
|
|
3760
|
-
*/
|
|
3761
3834
|
isNodeBackedToolConfig(config$1) {
|
|
3762
3835
|
return config$1 instanceof __codemation_core.NodeBackedToolConfig || typeof config$1 === "object" && config$1 !== null && config$1.toolKind === "nodeBacked";
|
|
3763
3836
|
}
|
|
3764
|
-
/**
|
|
3765
|
-
* Callable tools use {@link CallableToolConfig#toolKind} for cross-package / JSON round-trip safety.
|
|
3766
|
-
*/
|
|
3767
3837
|
isCallableToolConfig(config$1) {
|
|
3768
3838
|
return config$1 instanceof __codemation_core.CallableToolConfig || typeof config$1 === "object" && config$1 !== null && config$1.toolKind === "callable";
|
|
3769
3839
|
}
|
|
@@ -3979,6 +4049,7 @@ var HttpRequest = class {
|
|
|
3979
4049
|
kind = "node";
|
|
3980
4050
|
type = HttpRequestNode;
|
|
3981
4051
|
execution = { hint: "local" };
|
|
4052
|
+
icon = "lucide:globe";
|
|
3982
4053
|
constructor(name, args = {}, retryPolicy = __codemation_core.RetryPolicy.defaultForHttp) {
|
|
3983
4054
|
this.name = name;
|
|
3984
4055
|
this.args = args;
|
|
@@ -4020,7 +4091,7 @@ var Aggregate = class {
|
|
|
4020
4091
|
type = AggregateNode;
|
|
4021
4092
|
execution = { hint: "local" };
|
|
4022
4093
|
keepBinaries = true;
|
|
4023
|
-
icon = "
|
|
4094
|
+
icon = "builtin:aggregate-rows";
|
|
4024
4095
|
constructor(name, aggregate, id) {
|
|
4025
4096
|
this.name = name;
|
|
4026
4097
|
this.aggregate = aggregate;
|
|
@@ -4115,7 +4186,7 @@ var If = class {
|
|
|
4115
4186
|
kind = "node";
|
|
4116
4187
|
type = IfNode;
|
|
4117
4188
|
execution = { hint: "local" };
|
|
4118
|
-
icon = "lucide:split";
|
|
4189
|
+
icon = "lucide:split@rot=90";
|
|
4119
4190
|
declaredOutputPorts = ["true", "false"];
|
|
4120
4191
|
constructor(name, predicate, id) {
|
|
4121
4192
|
this.name = name;
|
|
@@ -4180,7 +4251,7 @@ var Split = class {
|
|
|
4180
4251
|
* Mirrors {@link MapData}'s empty-output behavior.
|
|
4181
4252
|
*/
|
|
4182
4253
|
continueWhenEmptyOutput = true;
|
|
4183
|
-
icon = "
|
|
4254
|
+
icon = "builtin:split-rows";
|
|
4184
4255
|
constructor(name, getElements, id) {
|
|
4185
4256
|
this.name = name;
|
|
4186
4257
|
this.getElements = getElements;
|
|
@@ -4251,6 +4322,7 @@ var MapData = class {
|
|
|
4251
4322
|
execution = { hint: "local" };
|
|
4252
4323
|
/** Zero mapped items should still allow downstream nodes to run. */
|
|
4253
4324
|
continueWhenEmptyOutput = true;
|
|
4325
|
+
icon = "lucide:square-pen";
|
|
4254
4326
|
keepBinaries;
|
|
4255
4327
|
constructor(name, map, options = {}) {
|
|
4256
4328
|
this.name = name;
|
|
@@ -4320,7 +4392,7 @@ MergeNode = __decorate([(0, __codemation_core.node)({ packageName: "@codemation/
|
|
|
4320
4392
|
var Merge = class {
|
|
4321
4393
|
kind = "node";
|
|
4322
4394
|
type = MergeNode;
|
|
4323
|
-
icon = "lucide:
|
|
4395
|
+
icon = "lucide:merge@rot=90";
|
|
4324
4396
|
constructor(name, cfg = { mode: "passThrough" }, id) {
|
|
4325
4397
|
this.name = name;
|
|
4326
4398
|
this.cfg = cfg;
|
|
@@ -4345,6 +4417,7 @@ var NoOp = class {
|
|
|
4345
4417
|
kind = "node";
|
|
4346
4418
|
type = NoOpNode;
|
|
4347
4419
|
execution = { hint: "local" };
|
|
4420
|
+
icon = "lucide:circle-dashed";
|
|
4348
4421
|
constructor(name = "NoOp", id) {
|
|
4349
4422
|
this.name = name;
|
|
4350
4423
|
this.id = id;
|
|
@@ -4450,6 +4523,7 @@ var Wait = class {
|
|
|
4450
4523
|
execution = { hint: "local" };
|
|
4451
4524
|
/** Pass-through empty batches should still advance to downstream nodes. */
|
|
4452
4525
|
continueWhenEmptyOutput = true;
|
|
4526
|
+
icon = "lucide:hourglass";
|
|
4453
4527
|
constructor(name, milliseconds, id) {
|
|
4454
4528
|
this.name = name;
|
|
4455
4529
|
this.milliseconds = milliseconds;
|
|
@@ -4500,7 +4574,7 @@ WebhookTriggerNode = __decorate([(0, __codemation_core.node)({ packageName: "@co
|
|
|
4500
4574
|
var WebhookTrigger = class WebhookTrigger {
|
|
4501
4575
|
kind = "trigger";
|
|
4502
4576
|
type = WebhookTriggerNode;
|
|
4503
|
-
icon = "lucide:
|
|
4577
|
+
icon = "lucide:webhook";
|
|
4504
4578
|
constructor(name, args, handler = WebhookTrigger.defaultHandler, id) {
|
|
4505
4579
|
this.name = name;
|
|
4506
4580
|
this.args = args;
|
|
@@ -4983,13 +5057,13 @@ Object.defineProperty(exports, 'OpenAIChatModelFactory', {
|
|
|
4983
5057
|
return OpenAIChatModelFactory;
|
|
4984
5058
|
}
|
|
4985
5059
|
});
|
|
4986
|
-
|
|
5060
|
+
exports.OpenAiChatModelPresets = OpenAiChatModelPresets;
|
|
5061
|
+
Object.defineProperty(exports, 'OpenAiStrictJsonSchemaFactory', {
|
|
4987
5062
|
enumerable: true,
|
|
4988
5063
|
get: function () {
|
|
4989
|
-
return
|
|
5064
|
+
return OpenAiStrictJsonSchemaFactory;
|
|
4990
5065
|
}
|
|
4991
5066
|
});
|
|
4992
|
-
exports.OpenAiChatModelPresets = OpenAiChatModelPresets;
|
|
4993
5067
|
exports.Split = Split;
|
|
4994
5068
|
Object.defineProperty(exports, 'SplitNode', {
|
|
4995
5069
|
enumerable: true,
|