@ai-sdk-tool/parser 1.0.0 → 1.0.2
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/dist/index.d.mts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +288 -0
- package/dist/index.js.map +1 -0
- package/{src/hermes-middleware.ts → dist/index.mjs} +98 -118
- package/dist/index.mjs.map +1 -0
- package/package.json +18 -11
- package/src/index.ts +0 -83
- package/src/utils.ts +0 -31
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as ai from 'ai';
|
|
2
|
+
import { LanguageModelV1Middleware } from 'ai';
|
|
3
|
+
|
|
4
|
+
declare const defaultTemplate: (tools: string) => string;
|
|
5
|
+
declare function createToolMiddleware({ toolCallTag, toolCallEndTag, toolResponseTag, toolResponseEndTag, toolSystemPromptTemplate, }: {
|
|
6
|
+
toolCallTag?: string;
|
|
7
|
+
toolCallEndTag?: string;
|
|
8
|
+
toolResponseTag?: string;
|
|
9
|
+
toolResponseEndTag?: string;
|
|
10
|
+
toolSystemPromptTemplate?: (tools: string) => string;
|
|
11
|
+
}): LanguageModelV1Middleware;
|
|
12
|
+
|
|
13
|
+
declare const gemmaToolMiddleware: ai.LanguageModelV1Middleware;
|
|
14
|
+
declare const hermesToolMiddleware: ai.LanguageModelV1Middleware;
|
|
15
|
+
|
|
16
|
+
export { createToolMiddleware, defaultTemplate, gemmaToolMiddleware, hermesToolMiddleware };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as ai from 'ai';
|
|
2
|
+
import { LanguageModelV1Middleware } from 'ai';
|
|
3
|
+
|
|
4
|
+
declare const defaultTemplate: (tools: string) => string;
|
|
5
|
+
declare function createToolMiddleware({ toolCallTag, toolCallEndTag, toolResponseTag, toolResponseEndTag, toolSystemPromptTemplate, }: {
|
|
6
|
+
toolCallTag?: string;
|
|
7
|
+
toolCallEndTag?: string;
|
|
8
|
+
toolResponseTag?: string;
|
|
9
|
+
toolResponseEndTag?: string;
|
|
10
|
+
toolSystemPromptTemplate?: (tools: string) => string;
|
|
11
|
+
}): LanguageModelV1Middleware;
|
|
12
|
+
|
|
13
|
+
declare const gemmaToolMiddleware: ai.LanguageModelV1Middleware;
|
|
14
|
+
declare const hermesToolMiddleware: ai.LanguageModelV1Middleware;
|
|
15
|
+
|
|
16
|
+
export { createToolMiddleware, defaultTemplate, gemmaToolMiddleware, hermesToolMiddleware };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
createToolMiddleware: () => createToolMiddleware,
|
|
34
|
+
defaultTemplate: () => defaultTemplate,
|
|
35
|
+
gemmaToolMiddleware: () => gemmaToolMiddleware,
|
|
36
|
+
hermesToolMiddleware: () => hermesToolMiddleware
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(index_exports);
|
|
39
|
+
|
|
40
|
+
// src/tool-call-middleware.ts
|
|
41
|
+
var import_ai = require("ai");
|
|
42
|
+
var RJSON = __toESM(require("relaxed-json"));
|
|
43
|
+
|
|
44
|
+
// src/utils.ts
|
|
45
|
+
function getPotentialStartIndex(text, searchedText) {
|
|
46
|
+
if (searchedText.length === 0) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const directIndex = text.indexOf(searchedText);
|
|
50
|
+
if (directIndex !== -1) {
|
|
51
|
+
return directIndex;
|
|
52
|
+
}
|
|
53
|
+
for (let i = text.length - 1; i >= 0; i--) {
|
|
54
|
+
const suffix = text.substring(i);
|
|
55
|
+
if (searchedText.startsWith(suffix)) {
|
|
56
|
+
return i;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/tool-call-middleware.ts
|
|
63
|
+
var defaultTemplate = (tools) => `You are a function calling AI model.
|
|
64
|
+
You are provided with function signatures within <tools></tools> XML tags.
|
|
65
|
+
You may call one or more functions to assist with the user query.
|
|
66
|
+
Don't make assumptions about what values to plug into functions.
|
|
67
|
+
Here are the available tools: <tools>${tools}</tools>
|
|
68
|
+
Use the following pydantic model json schema for each tool call you will make: {'title': 'FunctionCall', 'type': 'object', 'properties': {'arguments': {'title': 'Arguments', 'type': 'object'}, 'name': {'title': 'Name', 'type': 'string'}}, 'required': ['arguments', 'name']}
|
|
69
|
+
For each function call return a json object with function name and arguments within <tool_call></tool_call> XML tags as follows:
|
|
70
|
+
<tool_call>
|
|
71
|
+
{'arguments': <args-dict>, 'name': <function-name>}
|
|
72
|
+
</tool_call>`;
|
|
73
|
+
function createToolMiddleware({
|
|
74
|
+
toolCallTag = "<tool_call>",
|
|
75
|
+
toolCallEndTag = "</tool_call>",
|
|
76
|
+
toolResponseTag = "<tool_response>",
|
|
77
|
+
toolResponseEndTag = "</tool_response>",
|
|
78
|
+
toolSystemPromptTemplate = defaultTemplate
|
|
79
|
+
}) {
|
|
80
|
+
return {
|
|
81
|
+
middlewareVersion: "v1",
|
|
82
|
+
wrapStream: async ({ doStream }) => {
|
|
83
|
+
const { stream, ...rest } = await doStream();
|
|
84
|
+
let isFirstToolCall = true;
|
|
85
|
+
let isFirstText = true;
|
|
86
|
+
let afterSwitch = false;
|
|
87
|
+
let isToolCall = false;
|
|
88
|
+
let buffer = "";
|
|
89
|
+
let toolCallIndex = -1;
|
|
90
|
+
let toolCallBuffer = [];
|
|
91
|
+
const transformStream = new TransformStream({
|
|
92
|
+
transform(chunk, controller) {
|
|
93
|
+
if (chunk.type === "finish") {
|
|
94
|
+
if (toolCallBuffer.length > 0) {
|
|
95
|
+
toolCallBuffer.forEach((toolCall) => {
|
|
96
|
+
try {
|
|
97
|
+
const parsedToolCall = RJSON.parse(toolCall);
|
|
98
|
+
controller.enqueue({
|
|
99
|
+
type: "tool-call",
|
|
100
|
+
toolCallType: "function",
|
|
101
|
+
toolCallId: (0, import_ai.generateId)(),
|
|
102
|
+
toolName: parsedToolCall.name,
|
|
103
|
+
args: JSON.stringify(parsedToolCall.arguments)
|
|
104
|
+
});
|
|
105
|
+
} catch (e) {
|
|
106
|
+
console.error(`Error parsing tool call: ${toolCall}`, e);
|
|
107
|
+
controller.enqueue({
|
|
108
|
+
type: "text-delta",
|
|
109
|
+
textDelta: `Failed to parse tool call: ${e}`
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
controller.enqueue(chunk);
|
|
115
|
+
return;
|
|
116
|
+
} else if (chunk.type !== "text-delta") {
|
|
117
|
+
controller.enqueue(chunk);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
buffer += chunk.textDelta;
|
|
121
|
+
function publish(text) {
|
|
122
|
+
if (text.length > 0) {
|
|
123
|
+
const prefix = afterSwitch && (isToolCall ? !isFirstToolCall : !isFirstText) ? "\n" : "";
|
|
124
|
+
if (isToolCall) {
|
|
125
|
+
if (!toolCallBuffer[toolCallIndex]) {
|
|
126
|
+
toolCallBuffer[toolCallIndex] = "";
|
|
127
|
+
}
|
|
128
|
+
toolCallBuffer[toolCallIndex] += text;
|
|
129
|
+
} else {
|
|
130
|
+
controller.enqueue({
|
|
131
|
+
type: "text-delta",
|
|
132
|
+
textDelta: prefix + text
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
afterSwitch = false;
|
|
136
|
+
if (isToolCall) {
|
|
137
|
+
isFirstToolCall = false;
|
|
138
|
+
} else {
|
|
139
|
+
isFirstText = false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
do {
|
|
144
|
+
const nextTag = isToolCall ? toolCallEndTag : toolCallTag;
|
|
145
|
+
const startIndex = getPotentialStartIndex(buffer, nextTag);
|
|
146
|
+
if (startIndex == null) {
|
|
147
|
+
publish(buffer);
|
|
148
|
+
buffer = "";
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
publish(buffer.slice(0, startIndex));
|
|
152
|
+
const foundFullMatch = startIndex + nextTag.length <= buffer.length;
|
|
153
|
+
if (foundFullMatch) {
|
|
154
|
+
buffer = buffer.slice(startIndex + nextTag.length);
|
|
155
|
+
toolCallIndex++;
|
|
156
|
+
isToolCall = !isToolCall;
|
|
157
|
+
afterSwitch = true;
|
|
158
|
+
} else {
|
|
159
|
+
buffer = buffer.slice(startIndex);
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
} while (true);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
return {
|
|
166
|
+
stream: stream.pipeThrough(transformStream),
|
|
167
|
+
...rest
|
|
168
|
+
};
|
|
169
|
+
},
|
|
170
|
+
wrapGenerate: async ({ doGenerate }) => {
|
|
171
|
+
var _a;
|
|
172
|
+
const result = await doGenerate();
|
|
173
|
+
if (!((_a = result.text) == null ? void 0 : _a.includes(toolCallTag))) {
|
|
174
|
+
return result;
|
|
175
|
+
}
|
|
176
|
+
const toolCallRegex = new RegExp(
|
|
177
|
+
`${toolCallTag}(.*?)(?:${toolCallEndTag}|$)`,
|
|
178
|
+
"gs"
|
|
179
|
+
);
|
|
180
|
+
const matches = [...result.text.matchAll(toolCallRegex)];
|
|
181
|
+
const function_call_tuples = matches.map((match) => match[1] || match[2]);
|
|
182
|
+
return {
|
|
183
|
+
...result,
|
|
184
|
+
// TODO: Return the remaining value after extracting the tool call tag.
|
|
185
|
+
text: "",
|
|
186
|
+
toolCalls: function_call_tuples.map((toolCall) => {
|
|
187
|
+
const parsedToolCall = RJSON.parse(toolCall);
|
|
188
|
+
const toolName = parsedToolCall.name;
|
|
189
|
+
const args = parsedToolCall.arguments;
|
|
190
|
+
return {
|
|
191
|
+
toolCallType: "function",
|
|
192
|
+
toolCallId: (0, import_ai.generateId)(),
|
|
193
|
+
toolName,
|
|
194
|
+
args: RJSON.stringify(args)
|
|
195
|
+
};
|
|
196
|
+
})
|
|
197
|
+
};
|
|
198
|
+
},
|
|
199
|
+
transformParams: async ({ params }) => {
|
|
200
|
+
const processedPrompt = params.prompt.map((message) => {
|
|
201
|
+
if (message.role === "assistant") {
|
|
202
|
+
return {
|
|
203
|
+
role: "assistant",
|
|
204
|
+
content: message.content.map((content) => {
|
|
205
|
+
if (content.type === "tool-call") {
|
|
206
|
+
return {
|
|
207
|
+
type: "text",
|
|
208
|
+
text: `${toolCallTag}${JSON.stringify({
|
|
209
|
+
arguments: content.args,
|
|
210
|
+
name: content.toolName
|
|
211
|
+
})}${toolCallEndTag}`
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return content;
|
|
215
|
+
})
|
|
216
|
+
};
|
|
217
|
+
} else if (message.role === "tool") {
|
|
218
|
+
return {
|
|
219
|
+
role: "user",
|
|
220
|
+
content: [
|
|
221
|
+
{
|
|
222
|
+
type: "text",
|
|
223
|
+
text: message.content.map(
|
|
224
|
+
(content) => `${toolResponseTag}${JSON.stringify({
|
|
225
|
+
toolName: content.toolName,
|
|
226
|
+
result: content.result
|
|
227
|
+
})}${toolResponseEndTag}`
|
|
228
|
+
).join("\n")
|
|
229
|
+
}
|
|
230
|
+
]
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
return message;
|
|
234
|
+
});
|
|
235
|
+
const originalToolDefinitions = params.mode.type === "regular" && params.mode.tools ? params.mode.tools : {};
|
|
236
|
+
const HermesPrompt = toolSystemPromptTemplate(
|
|
237
|
+
JSON.stringify(Object.entries(originalToolDefinitions))
|
|
238
|
+
);
|
|
239
|
+
const toolSystemPrompt = processedPrompt[0].role === "system" ? [
|
|
240
|
+
{
|
|
241
|
+
role: "system",
|
|
242
|
+
content: HermesPrompt + "\n\n" + processedPrompt[0].content
|
|
243
|
+
},
|
|
244
|
+
...processedPrompt.slice(1)
|
|
245
|
+
] : [
|
|
246
|
+
{
|
|
247
|
+
role: "system",
|
|
248
|
+
content: HermesPrompt
|
|
249
|
+
},
|
|
250
|
+
...processedPrompt
|
|
251
|
+
];
|
|
252
|
+
return {
|
|
253
|
+
...params,
|
|
254
|
+
mode: {
|
|
255
|
+
// set the mode back to regular and remove the default tools.
|
|
256
|
+
type: "regular"
|
|
257
|
+
},
|
|
258
|
+
prompt: toolSystemPrompt
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// src/index.ts
|
|
265
|
+
var gemmaToolMiddleware = createToolMiddleware({
|
|
266
|
+
toolSystemPromptTemplate(tools) {
|
|
267
|
+
return `You have access to functions. If you decide to invoke any of the function(s),
|
|
268
|
+
you MUST put it in the format of
|
|
269
|
+
\`\`\`tool_call
|
|
270
|
+
{'name': <function-name>, 'arguments': <args-dict>}
|
|
271
|
+
\`\`\`
|
|
272
|
+
You SHOULD NOT include any other text in the response if you call a function
|
|
273
|
+
${tools}`;
|
|
274
|
+
},
|
|
275
|
+
toolCallTag: "```tool_call\n",
|
|
276
|
+
toolCallEndTag: "```",
|
|
277
|
+
toolResponseTag: "```tool_response\n",
|
|
278
|
+
toolResponseEndTag: "\n```"
|
|
279
|
+
});
|
|
280
|
+
var hermesToolMiddleware = createToolMiddleware({});
|
|
281
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
282
|
+
0 && (module.exports = {
|
|
283
|
+
createToolMiddleware,
|
|
284
|
+
defaultTemplate,
|
|
285
|
+
gemmaToolMiddleware,
|
|
286
|
+
hermesToolMiddleware
|
|
287
|
+
});
|
|
288
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/tool-call-middleware.ts","../src/utils.ts"],"sourcesContent":["import { defaultTemplate, createToolMiddleware } from \"./tool-call-middleware\";\n\nconst gemmaToolMiddleware = createToolMiddleware({\n toolSystemPromptTemplate(tools) {\n return `You have access to functions. If you decide to invoke any of the function(s),\n you MUST put it in the format of\n \\`\\`\\`tool_call\n {'name': <function-name>, 'arguments': <args-dict>}\n \\`\\`\\`\n You SHOULD NOT include any other text in the response if you call a function\n ${tools}`;\n },\n toolCallTag: \"```tool_call\\n\",\n toolCallEndTag: \"```\",\n toolResponseTag: \"```tool_response\\n\",\n toolResponseEndTag: \"\\n```\",\n});\n\nconst hermesToolMiddleware = createToolMiddleware({});\n\nexport {\n defaultTemplate,\n gemmaToolMiddleware,\n hermesToolMiddleware,\n createToolMiddleware,\n};\n","import {\n generateId,\n LanguageModelV1Middleware,\n LanguageModelV1Prompt,\n LanguageModelV1StreamPart,\n} from \"ai\";\nimport * as RJSON from \"relaxed-json\";\nimport { getPotentialStartIndex } from \"./utils\";\n\nexport const defaultTemplate = (tools: string) =>\n `You are a function calling AI model.\nYou are provided with function signatures within <tools></tools> XML tags.\nYou may call one or more functions to assist with the user query.\nDon't make assumptions about what values to plug into functions.\nHere are the available tools: <tools>${tools}</tools>\nUse the following pydantic model json schema for each tool call you will make: {'title': 'FunctionCall', 'type': 'object', 'properties': {'arguments': {'title': 'Arguments', 'type': 'object'}, 'name': {'title': 'Name', 'type': 'string'}}, 'required': ['arguments', 'name']}\nFor each function call return a json object with function name and arguments within <tool_call></tool_call> XML tags as follows:\n<tool_call>\n{'arguments': <args-dict>, 'name': <function-name>}\n</tool_call>`;\n\nexport function createToolMiddleware({\n toolCallTag = \"<tool_call>\",\n toolCallEndTag = \"</tool_call>\",\n toolResponseTag = \"<tool_response>\",\n toolResponseEndTag = \"</tool_response>\",\n toolSystemPromptTemplate = defaultTemplate,\n}: {\n toolCallTag?: string;\n toolCallEndTag?: string;\n toolResponseTag?: string;\n toolResponseEndTag?: string;\n toolSystemPromptTemplate?: (tools: string) => string;\n}): LanguageModelV1Middleware {\n return {\n middlewareVersion: \"v1\",\n wrapStream: async ({ doStream }) => {\n const { stream, ...rest } = await doStream();\n\n let isFirstToolCall = true;\n let isFirstText = true;\n let afterSwitch = false;\n let isToolCall = false;\n let buffer = \"\";\n\n let toolCallIndex = -1;\n let toolCallBuffer: string[] = [];\n\n const transformStream = new TransformStream<\n LanguageModelV1StreamPart,\n LanguageModelV1StreamPart\n >({\n transform(chunk, controller) {\n if (chunk.type === \"finish\") {\n if (toolCallBuffer.length > 0) {\n toolCallBuffer.forEach((toolCall) => {\n try {\n const parsedToolCall = RJSON.parse(toolCall) as {\n name: string;\n arguments: string;\n };\n\n controller.enqueue({\n type: \"tool-call\",\n toolCallType: \"function\",\n toolCallId: generateId(),\n toolName: parsedToolCall.name,\n args: JSON.stringify(parsedToolCall.arguments),\n });\n } catch (e) {\n console.error(`Error parsing tool call: ${toolCall}`, e);\n\n controller.enqueue({\n type: \"text-delta\",\n textDelta: `Failed to parse tool call: ${e}`,\n });\n }\n });\n }\n\n // stop token\n controller.enqueue(chunk);\n\n return;\n } else if (chunk.type !== \"text-delta\") {\n controller.enqueue(chunk);\n return;\n }\n\n buffer += chunk.textDelta;\n\n function publish(text: string) {\n if (text.length > 0) {\n const prefix =\n afterSwitch && (isToolCall ? !isFirstToolCall : !isFirstText)\n ? \"\\n\" // separator\n : \"\";\n\n if (isToolCall) {\n if (!toolCallBuffer[toolCallIndex]) {\n toolCallBuffer[toolCallIndex] = \"\";\n }\n\n toolCallBuffer[toolCallIndex] += text;\n } else {\n controller.enqueue({\n type: \"text-delta\",\n textDelta: prefix + text,\n });\n }\n\n afterSwitch = false;\n\n if (isToolCall) {\n isFirstToolCall = false;\n } else {\n isFirstText = false;\n }\n }\n }\n\n do {\n const nextTag = isToolCall ? toolCallEndTag : toolCallTag;\n const startIndex = getPotentialStartIndex(buffer, nextTag);\n\n // no opening or closing tag found, publish the buffer\n if (startIndex == null) {\n publish(buffer);\n buffer = \"\";\n break;\n }\n\n // publish text before the tag\n publish(buffer.slice(0, startIndex));\n\n const foundFullMatch = startIndex + nextTag.length <= buffer.length;\n\n if (foundFullMatch) {\n buffer = buffer.slice(startIndex + nextTag.length);\n toolCallIndex++;\n isToolCall = !isToolCall;\n afterSwitch = true;\n } else {\n buffer = buffer.slice(startIndex);\n break;\n }\n } while (true);\n },\n });\n\n return {\n stream: stream.pipeThrough(transformStream),\n ...rest,\n };\n },\n wrapGenerate: async ({ doGenerate }) => {\n const result = await doGenerate();\n\n if (!result.text?.includes(toolCallTag)) {\n return result;\n }\n\n const toolCallRegex = new RegExp(\n `${toolCallTag}(.*?)(?:${toolCallEndTag}|$)`,\n \"gs\"\n );\n const matches = [...result.text.matchAll(toolCallRegex)];\n const function_call_tuples = matches.map((match) => match[1] || match[2]);\n\n return {\n ...result,\n // TODO: Return the remaining value after extracting the tool call tag.\n text: \"\",\n toolCalls: function_call_tuples.map((toolCall) => {\n const parsedToolCall = RJSON.parse(toolCall) as {\n name: string;\n arguments: string;\n };\n\n const toolName = parsedToolCall.name;\n const args = parsedToolCall.arguments;\n\n return {\n toolCallType: \"function\",\n toolCallId: generateId(),\n toolName: toolName,\n args: RJSON.stringify(args),\n };\n }),\n };\n },\n\n transformParams: async ({ params }) => {\n const processedPrompt = params.prompt.map((message) => {\n if (message.role === \"assistant\") {\n return {\n role: \"assistant\",\n content: message.content.map((content) => {\n if (content.type === \"tool-call\") {\n return {\n type: \"text\",\n text: `${toolCallTag}${JSON.stringify({\n arguments: content.args,\n name: content.toolName,\n })}${toolCallEndTag}`,\n };\n }\n\n return content;\n }),\n };\n } else if (message.role === \"tool\") {\n return {\n role: \"user\",\n content: [\n {\n type: \"text\",\n text: message.content\n .map(\n (content) =>\n `${toolResponseTag}${JSON.stringify({\n toolName: content.toolName,\n result: content.result,\n })}${toolResponseEndTag}`\n )\n .join(\"\\n\"),\n },\n ],\n };\n }\n\n return message;\n }) as LanguageModelV1Prompt;\n\n // Appropriate fixes are needed as they are disappearing in LanguageModelV2\n const originalToolDefinitions =\n params.mode.type === \"regular\" && params.mode.tools\n ? params.mode.tools\n : {};\n\n const HermesPrompt = toolSystemPromptTemplate(\n JSON.stringify(Object.entries(originalToolDefinitions))\n );\n\n const toolSystemPrompt: LanguageModelV1Prompt =\n processedPrompt[0].role === \"system\"\n ? [\n {\n role: \"system\",\n content: HermesPrompt + \"\\n\\n\" + processedPrompt[0].content,\n },\n ...processedPrompt.slice(1),\n ]\n : [\n {\n role: \"system\",\n content: HermesPrompt,\n },\n ...processedPrompt,\n ];\n\n return {\n ...params,\n mode: {\n // set the mode back to regular and remove the default tools.\n type: \"regular\",\n },\n prompt: toolSystemPrompt,\n };\n },\n };\n}\n","/**\n * Returns the index of the start of the searchedText in the text, or null if it\n * is not found.\n * ref: https://github.com/vercel/ai/blob/452bf12f0be9cb398d4af85a006bca13c8ce36d8/packages/ai/core/util/get-potential-start-index.ts\n */\nexport function getPotentialStartIndex(\n text: string,\n searchedText: string\n): number | null {\n // Return null immediately if searchedText is empty.\n if (searchedText.length === 0) {\n return null;\n }\n\n // Check if the searchedText exists as a direct substring of text.\n const directIndex = text.indexOf(searchedText);\n if (directIndex !== -1) {\n return directIndex;\n }\n\n // Otherwise, look for the largest suffix of \"text\" that matches\n // a prefix of \"searchedText\". We go from the end of text inward.\n for (let i = text.length - 1; i >= 0; i--) {\n const suffix = text.substring(i);\n if (searchedText.startsWith(suffix)) {\n return i;\n }\n }\n\n return null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,gBAKO;AACP,YAAuB;;;ACDhB,SAAS,uBACd,MACA,cACe;AAEf,MAAI,aAAa,WAAW,GAAG;AAC7B,WAAO;AAAA,EACT;AAGA,QAAM,cAAc,KAAK,QAAQ,YAAY;AAC7C,MAAI,gBAAgB,IAAI;AACtB,WAAO;AAAA,EACT;AAIA,WAAS,IAAI,KAAK,SAAS,GAAG,KAAK,GAAG,KAAK;AACzC,UAAM,SAAS,KAAK,UAAU,CAAC;AAC/B,QAAI,aAAa,WAAW,MAAM,GAAG;AACnC,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;;;ADrBO,IAAM,kBAAkB,CAAC,UAC9B;AAAA;AAAA;AAAA;AAAA,uCAIqC,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAOrC,SAAS,qBAAqB;AAAA,EACnC,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,qBAAqB;AAAA,EACrB,2BAA2B;AAC7B,GAM8B;AAC5B,SAAO;AAAA,IACL,mBAAmB;AAAA,IACnB,YAAY,OAAO,EAAE,SAAS,MAAM;AAClC,YAAM,EAAE,QAAQ,GAAG,KAAK,IAAI,MAAM,SAAS;AAE3C,UAAI,kBAAkB;AACtB,UAAI,cAAc;AAClB,UAAI,cAAc;AAClB,UAAI,aAAa;AACjB,UAAI,SAAS;AAEb,UAAI,gBAAgB;AACpB,UAAI,iBAA2B,CAAC;AAEhC,YAAM,kBAAkB,IAAI,gBAG1B;AAAA,QACA,UAAU,OAAO,YAAY;AAC3B,cAAI,MAAM,SAAS,UAAU;AAC3B,gBAAI,eAAe,SAAS,GAAG;AAC7B,6BAAe,QAAQ,CAAC,aAAa;AACnC,oBAAI;AACF,wBAAM,iBAAuB,YAAM,QAAQ;AAK3C,6BAAW,QAAQ;AAAA,oBACjB,MAAM;AAAA,oBACN,cAAc;AAAA,oBACd,gBAAY,sBAAW;AAAA,oBACvB,UAAU,eAAe;AAAA,oBACzB,MAAM,KAAK,UAAU,eAAe,SAAS;AAAA,kBAC/C,CAAC;AAAA,gBACH,SAAS,GAAG;AACV,0BAAQ,MAAM,4BAA4B,QAAQ,IAAI,CAAC;AAEvD,6BAAW,QAAQ;AAAA,oBACjB,MAAM;AAAA,oBACN,WAAW,8BAA8B,CAAC;AAAA,kBAC5C,CAAC;AAAA,gBACH;AAAA,cACF,CAAC;AAAA,YACH;AAGA,uBAAW,QAAQ,KAAK;AAExB;AAAA,UACF,WAAW,MAAM,SAAS,cAAc;AACtC,uBAAW,QAAQ,KAAK;AACxB;AAAA,UACF;AAEA,oBAAU,MAAM;AAEhB,mBAAS,QAAQ,MAAc;AAC7B,gBAAI,KAAK,SAAS,GAAG;AACnB,oBAAM,SACJ,gBAAgB,aAAa,CAAC,kBAAkB,CAAC,eAC7C,OACA;AAEN,kBAAI,YAAY;AACd,oBAAI,CAAC,eAAe,aAAa,GAAG;AAClC,iCAAe,aAAa,IAAI;AAAA,gBAClC;AAEA,+BAAe,aAAa,KAAK;AAAA,cACnC,OAAO;AACL,2BAAW,QAAQ;AAAA,kBACjB,MAAM;AAAA,kBACN,WAAW,SAAS;AAAA,gBACtB,CAAC;AAAA,cACH;AAEA,4BAAc;AAEd,kBAAI,YAAY;AACd,kCAAkB;AAAA,cACpB,OAAO;AACL,8BAAc;AAAA,cAChB;AAAA,YACF;AAAA,UACF;AAEA,aAAG;AACD,kBAAM,UAAU,aAAa,iBAAiB;AAC9C,kBAAM,aAAa,uBAAuB,QAAQ,OAAO;AAGzD,gBAAI,cAAc,MAAM;AACtB,sBAAQ,MAAM;AACd,uBAAS;AACT;AAAA,YACF;AAGA,oBAAQ,OAAO,MAAM,GAAG,UAAU,CAAC;AAEnC,kBAAM,iBAAiB,aAAa,QAAQ,UAAU,OAAO;AAE7D,gBAAI,gBAAgB;AAClB,uBAAS,OAAO,MAAM,aAAa,QAAQ,MAAM;AACjD;AACA,2BAAa,CAAC;AACd,4BAAc;AAAA,YAChB,OAAO;AACL,uBAAS,OAAO,MAAM,UAAU;AAChC;AAAA,YACF;AAAA,UACF,SAAS;AAAA,QACX;AAAA,MACF,CAAC;AAED,aAAO;AAAA,QACL,QAAQ,OAAO,YAAY,eAAe;AAAA,QAC1C,GAAG;AAAA,MACL;AAAA,IACF;AAAA,IACA,cAAc,OAAO,EAAE,WAAW,MAAM;AA3J5C;AA4JM,YAAM,SAAS,MAAM,WAAW;AAEhC,UAAI,GAAC,YAAO,SAAP,mBAAa,SAAS,eAAc;AACvC,eAAO;AAAA,MACT;AAEA,YAAM,gBAAgB,IAAI;AAAA,QACxB,GAAG,WAAW,WAAW,cAAc;AAAA,QACvC;AAAA,MACF;AACA,YAAM,UAAU,CAAC,GAAG,OAAO,KAAK,SAAS,aAAa,CAAC;AACvD,YAAM,uBAAuB,QAAQ,IAAI,CAAC,UAAU,MAAM,CAAC,KAAK,MAAM,CAAC,CAAC;AAExE,aAAO;AAAA,QACL,GAAG;AAAA;AAAA,QAEH,MAAM;AAAA,QACN,WAAW,qBAAqB,IAAI,CAAC,aAAa;AAChD,gBAAM,iBAAuB,YAAM,QAAQ;AAK3C,gBAAM,WAAW,eAAe;AAChC,gBAAM,OAAO,eAAe;AAE5B,iBAAO;AAAA,YACL,cAAc;AAAA,YACd,gBAAY,sBAAW;AAAA,YACvB;AAAA,YACA,MAAY,gBAAU,IAAI;AAAA,UAC5B;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,IAEA,iBAAiB,OAAO,EAAE,OAAO,MAAM;AACrC,YAAM,kBAAkB,OAAO,OAAO,IAAI,CAAC,YAAY;AACrD,YAAI,QAAQ,SAAS,aAAa;AAChC,iBAAO;AAAA,YACL,MAAM;AAAA,YACN,SAAS,QAAQ,QAAQ,IAAI,CAAC,YAAY;AACxC,kBAAI,QAAQ,SAAS,aAAa;AAChC,uBAAO;AAAA,kBACL,MAAM;AAAA,kBACN,MAAM,GAAG,WAAW,GAAG,KAAK,UAAU;AAAA,oBACpC,WAAW,QAAQ;AAAA,oBACnB,MAAM,QAAQ;AAAA,kBAChB,CAAC,CAAC,GAAG,cAAc;AAAA,gBACrB;AAAA,cACF;AAEA,qBAAO;AAAA,YACT,CAAC;AAAA,UACH;AAAA,QACF,WAAW,QAAQ,SAAS,QAAQ;AAClC,iBAAO;AAAA,YACL,MAAM;AAAA,YACN,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,QAAQ,QACX;AAAA,kBACC,CAAC,YACC,GAAG,eAAe,GAAG,KAAK,UAAU;AAAA,oBAClC,UAAU,QAAQ;AAAA,oBAClB,QAAQ,QAAQ;AAAA,kBAClB,CAAC,CAAC,GAAG,kBAAkB;AAAA,gBAC3B,EACC,KAAK,IAAI;AAAA,cACd;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAEA,eAAO;AAAA,MACT,CAAC;AAGD,YAAM,0BACJ,OAAO,KAAK,SAAS,aAAa,OAAO,KAAK,QAC1C,OAAO,KAAK,QACZ,CAAC;AAEP,YAAM,eAAe;AAAA,QACnB,KAAK,UAAU,OAAO,QAAQ,uBAAuB,CAAC;AAAA,MACxD;AAEA,YAAM,mBACJ,gBAAgB,CAAC,EAAE,SAAS,WACxB;AAAA,QACE;AAAA,UACE,MAAM;AAAA,UACN,SAAS,eAAe,SAAS,gBAAgB,CAAC,EAAE;AAAA,QACtD;AAAA,QACA,GAAG,gBAAgB,MAAM,CAAC;AAAA,MAC5B,IACA;AAAA,QACE;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,QACX;AAAA,QACA,GAAG;AAAA,MACL;AAEN,aAAO;AAAA,QACL,GAAG;AAAA,QACH,MAAM;AAAA;AAAA,UAEJ,MAAM;AAAA,QACR;AAAA,QACA,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AACF;;;AD7QA,IAAM,sBAAsB,qBAAqB;AAAA,EAC/C,yBAAyB,OAAO;AAC9B,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMP,KAAK;AAAA,EACP;AAAA,EACA,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,oBAAoB;AACtB,CAAC;AAED,IAAM,uBAAuB,qBAAqB,CAAC,CAAC;","names":[]}
|
|
@@ -1,114 +1,101 @@
|
|
|
1
|
+
// src/tool-call-middleware.ts
|
|
1
2
|
import {
|
|
2
|
-
generateId
|
|
3
|
-
LanguageModelV1Middleware,
|
|
4
|
-
LanguageModelV1Prompt,
|
|
5
|
-
LanguageModelV1StreamPart,
|
|
3
|
+
generateId
|
|
6
4
|
} from "ai";
|
|
7
5
|
import * as RJSON from "relaxed-json";
|
|
8
|
-
import { getPotentialStartIndex } from "./utils";
|
|
9
6
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
// src/utils.ts
|
|
8
|
+
function getPotentialStartIndex(text, searchedText) {
|
|
9
|
+
if (searchedText.length === 0) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
const directIndex = text.indexOf(searchedText);
|
|
13
|
+
if (directIndex !== -1) {
|
|
14
|
+
return directIndex;
|
|
15
|
+
}
|
|
16
|
+
for (let i = text.length - 1; i >= 0; i--) {
|
|
17
|
+
const suffix = text.substring(i);
|
|
18
|
+
if (searchedText.startsWith(suffix)) {
|
|
19
|
+
return i;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/tool-call-middleware.ts
|
|
26
|
+
var defaultTemplate = (tools) => `You are a function calling AI model.
|
|
27
|
+
You are provided with function signatures within <tools></tools> XML tags.
|
|
28
|
+
You may call one or more functions to assist with the user query.
|
|
29
|
+
Don't make assumptions about what values to plug into functions.
|
|
13
30
|
Here are the available tools: <tools>${tools}</tools>
|
|
14
31
|
Use the following pydantic model json schema for each tool call you will make: {'title': 'FunctionCall', 'type': 'object', 'properties': {'arguments': {'title': 'Arguments', 'type': 'object'}, 'name': {'title': 'Name', 'type': 'string'}}, 'required': ['arguments', 'name']}
|
|
15
32
|
For each function call return a json object with function name and arguments within <tool_call></tool_call> XML tags as follows:
|
|
16
33
|
<tool_call>
|
|
17
34
|
{'arguments': <args-dict>, 'name': <function-name>}
|
|
18
35
|
</tool_call>`;
|
|
19
|
-
|
|
20
|
-
export function hermesToolMiddleware({
|
|
36
|
+
function createToolMiddleware({
|
|
21
37
|
toolCallTag = "<tool_call>",
|
|
22
38
|
toolCallEndTag = "</tool_call>",
|
|
23
39
|
toolResponseTag = "<tool_response>",
|
|
24
40
|
toolResponseEndTag = "</tool_response>",
|
|
25
|
-
toolSystemPromptTemplate = defaultTemplate
|
|
26
|
-
}
|
|
27
|
-
toolCallTag?: string;
|
|
28
|
-
toolCallEndTag?: string;
|
|
29
|
-
toolResponseTag?: string;
|
|
30
|
-
toolResponseEndTag?: string;
|
|
31
|
-
toolSystemPromptTemplate?: (tools: string) => string;
|
|
32
|
-
}): LanguageModelV1Middleware {
|
|
41
|
+
toolSystemPromptTemplate = defaultTemplate
|
|
42
|
+
}) {
|
|
33
43
|
return {
|
|
34
44
|
middlewareVersion: "v1",
|
|
35
45
|
wrapStream: async ({ doStream }) => {
|
|
36
46
|
const { stream, ...rest } = await doStream();
|
|
37
|
-
|
|
38
47
|
let isFirstToolCall = true;
|
|
39
48
|
let isFirstText = true;
|
|
40
49
|
let afterSwitch = false;
|
|
41
50
|
let isToolCall = false;
|
|
42
51
|
let buffer = "";
|
|
43
|
-
|
|
44
52
|
let toolCallIndex = -1;
|
|
45
|
-
let toolCallBuffer
|
|
46
|
-
|
|
47
|
-
const transformStream = new TransformStream<
|
|
48
|
-
LanguageModelV1StreamPart,
|
|
49
|
-
LanguageModelV1StreamPart
|
|
50
|
-
>({
|
|
53
|
+
let toolCallBuffer = [];
|
|
54
|
+
const transformStream = new TransformStream({
|
|
51
55
|
transform(chunk, controller) {
|
|
52
56
|
if (chunk.type === "finish") {
|
|
53
57
|
if (toolCallBuffer.length > 0) {
|
|
54
58
|
toolCallBuffer.forEach((toolCall) => {
|
|
55
59
|
try {
|
|
56
|
-
const parsedToolCall = RJSON.parse(toolCall)
|
|
57
|
-
name: string;
|
|
58
|
-
arguments: string;
|
|
59
|
-
};
|
|
60
|
-
|
|
60
|
+
const parsedToolCall = RJSON.parse(toolCall);
|
|
61
61
|
controller.enqueue({
|
|
62
62
|
type: "tool-call",
|
|
63
63
|
toolCallType: "function",
|
|
64
64
|
toolCallId: generateId(),
|
|
65
65
|
toolName: parsedToolCall.name,
|
|
66
|
-
args: JSON.stringify(parsedToolCall.arguments)
|
|
66
|
+
args: JSON.stringify(parsedToolCall.arguments)
|
|
67
67
|
});
|
|
68
68
|
} catch (e) {
|
|
69
69
|
console.error(`Error parsing tool call: ${toolCall}`, e);
|
|
70
|
-
|
|
71
70
|
controller.enqueue({
|
|
72
71
|
type: "text-delta",
|
|
73
|
-
textDelta: `Failed to parse tool call: ${e
|
|
72
|
+
textDelta: `Failed to parse tool call: ${e}`
|
|
74
73
|
});
|
|
75
74
|
}
|
|
76
75
|
});
|
|
77
76
|
}
|
|
78
|
-
|
|
79
|
-
// stop token
|
|
80
77
|
controller.enqueue(chunk);
|
|
81
|
-
|
|
82
78
|
return;
|
|
83
79
|
} else if (chunk.type !== "text-delta") {
|
|
84
80
|
controller.enqueue(chunk);
|
|
85
81
|
return;
|
|
86
82
|
}
|
|
87
|
-
|
|
88
83
|
buffer += chunk.textDelta;
|
|
89
|
-
|
|
90
|
-
function publish(text: string) {
|
|
84
|
+
function publish(text) {
|
|
91
85
|
if (text.length > 0) {
|
|
92
|
-
const prefix =
|
|
93
|
-
afterSwitch && (isToolCall ? !isFirstToolCall : !isFirstText)
|
|
94
|
-
? "\n" // separator
|
|
95
|
-
: "";
|
|
96
|
-
|
|
86
|
+
const prefix = afterSwitch && (isToolCall ? !isFirstToolCall : !isFirstText) ? "\n" : "";
|
|
97
87
|
if (isToolCall) {
|
|
98
88
|
if (!toolCallBuffer[toolCallIndex]) {
|
|
99
89
|
toolCallBuffer[toolCallIndex] = "";
|
|
100
90
|
}
|
|
101
|
-
|
|
102
91
|
toolCallBuffer[toolCallIndex] += text;
|
|
103
92
|
} else {
|
|
104
93
|
controller.enqueue({
|
|
105
94
|
type: "text-delta",
|
|
106
|
-
textDelta: prefix + text
|
|
95
|
+
textDelta: prefix + text
|
|
107
96
|
});
|
|
108
97
|
}
|
|
109
|
-
|
|
110
98
|
afterSwitch = false;
|
|
111
|
-
|
|
112
99
|
if (isToolCall) {
|
|
113
100
|
isFirstToolCall = false;
|
|
114
101
|
} else {
|
|
@@ -116,23 +103,16 @@ export function hermesToolMiddleware({
|
|
|
116
103
|
}
|
|
117
104
|
}
|
|
118
105
|
}
|
|
119
|
-
|
|
120
106
|
do {
|
|
121
107
|
const nextTag = isToolCall ? toolCallEndTag : toolCallTag;
|
|
122
108
|
const startIndex = getPotentialStartIndex(buffer, nextTag);
|
|
123
|
-
|
|
124
|
-
// no opening or closing tag found, publish the buffer
|
|
125
109
|
if (startIndex == null) {
|
|
126
110
|
publish(buffer);
|
|
127
111
|
buffer = "";
|
|
128
112
|
break;
|
|
129
113
|
}
|
|
130
|
-
|
|
131
|
-
// publish text before the tag
|
|
132
114
|
publish(buffer.slice(0, startIndex));
|
|
133
|
-
|
|
134
115
|
const foundFullMatch = startIndex + nextTag.length <= buffer.length;
|
|
135
|
-
|
|
136
116
|
if (foundFullMatch) {
|
|
137
117
|
buffer = buffer.slice(startIndex + nextTag.length);
|
|
138
118
|
toolCallIndex++;
|
|
@@ -143,51 +123,42 @@ export function hermesToolMiddleware({
|
|
|
143
123
|
break;
|
|
144
124
|
}
|
|
145
125
|
} while (true);
|
|
146
|
-
}
|
|
126
|
+
}
|
|
147
127
|
});
|
|
148
|
-
|
|
149
128
|
return {
|
|
150
129
|
stream: stream.pipeThrough(transformStream),
|
|
151
|
-
...rest
|
|
130
|
+
...rest
|
|
152
131
|
};
|
|
153
132
|
},
|
|
154
133
|
wrapGenerate: async ({ doGenerate }) => {
|
|
134
|
+
var _a;
|
|
155
135
|
const result = await doGenerate();
|
|
156
|
-
|
|
157
|
-
if (!result.text?.includes(toolCallTag)) {
|
|
136
|
+
if (!((_a = result.text) == null ? void 0 : _a.includes(toolCallTag))) {
|
|
158
137
|
return result;
|
|
159
138
|
}
|
|
160
|
-
|
|
161
139
|
const toolCallRegex = new RegExp(
|
|
162
140
|
`${toolCallTag}(.*?)(?:${toolCallEndTag}|$)`,
|
|
163
141
|
"gs"
|
|
164
142
|
);
|
|
165
143
|
const matches = [...result.text.matchAll(toolCallRegex)];
|
|
166
144
|
const function_call_tuples = matches.map((match) => match[1] || match[2]);
|
|
167
|
-
|
|
168
145
|
return {
|
|
169
146
|
...result,
|
|
170
147
|
// TODO: Return the remaining value after extracting the tool call tag.
|
|
171
148
|
text: "",
|
|
172
149
|
toolCalls: function_call_tuples.map((toolCall) => {
|
|
173
|
-
const parsedToolCall = RJSON.parse(toolCall)
|
|
174
|
-
name: string;
|
|
175
|
-
arguments: string;
|
|
176
|
-
};
|
|
177
|
-
|
|
150
|
+
const parsedToolCall = RJSON.parse(toolCall);
|
|
178
151
|
const toolName = parsedToolCall.name;
|
|
179
152
|
const args = parsedToolCall.arguments;
|
|
180
|
-
|
|
181
153
|
return {
|
|
182
154
|
toolCallType: "function",
|
|
183
155
|
toolCallId: generateId(),
|
|
184
|
-
toolName
|
|
185
|
-
args: RJSON.stringify(args)
|
|
156
|
+
toolName,
|
|
157
|
+
args: RJSON.stringify(args)
|
|
186
158
|
};
|
|
187
|
-
})
|
|
159
|
+
})
|
|
188
160
|
};
|
|
189
161
|
},
|
|
190
|
-
|
|
191
162
|
transformParams: async ({ params }) => {
|
|
192
163
|
const processedPrompt = params.prompt.map((message) => {
|
|
193
164
|
if (message.role === "assistant") {
|
|
@@ -199,13 +170,12 @@ export function hermesToolMiddleware({
|
|
|
199
170
|
type: "text",
|
|
200
171
|
text: `${toolCallTag}${JSON.stringify({
|
|
201
172
|
arguments: content.args,
|
|
202
|
-
name: content.toolName
|
|
203
|
-
})}${toolCallEndTag}
|
|
173
|
+
name: content.toolName
|
|
174
|
+
})}${toolCallEndTag}`
|
|
204
175
|
};
|
|
205
176
|
}
|
|
206
|
-
|
|
207
177
|
return content;
|
|
208
|
-
})
|
|
178
|
+
})
|
|
209
179
|
};
|
|
210
180
|
} else if (message.role === "tool") {
|
|
211
181
|
return {
|
|
@@ -213,58 +183,68 @@ export function hermesToolMiddleware({
|
|
|
213
183
|
content: [
|
|
214
184
|
{
|
|
215
185
|
type: "text",
|
|
216
|
-
text: message.content
|
|
217
|
-
.
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
.join("\n"),
|
|
225
|
-
},
|
|
226
|
-
],
|
|
186
|
+
text: message.content.map(
|
|
187
|
+
(content) => `${toolResponseTag}${JSON.stringify({
|
|
188
|
+
toolName: content.toolName,
|
|
189
|
+
result: content.result
|
|
190
|
+
})}${toolResponseEndTag}`
|
|
191
|
+
).join("\n")
|
|
192
|
+
}
|
|
193
|
+
]
|
|
227
194
|
};
|
|
228
195
|
}
|
|
229
|
-
|
|
230
196
|
return message;
|
|
231
|
-
})
|
|
232
|
-
|
|
233
|
-
// Appropriate fixes are needed as they are disappearing in LanguageModelV2
|
|
234
|
-
const originalToolDefinitions =
|
|
235
|
-
params.mode.type === "regular" && params.mode.tools
|
|
236
|
-
? params.mode.tools
|
|
237
|
-
: {};
|
|
238
|
-
|
|
197
|
+
});
|
|
198
|
+
const originalToolDefinitions = params.mode.type === "regular" && params.mode.tools ? params.mode.tools : {};
|
|
239
199
|
const HermesPrompt = toolSystemPromptTemplate(
|
|
240
200
|
JSON.stringify(Object.entries(originalToolDefinitions))
|
|
241
201
|
);
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
content: HermesPrompt,
|
|
256
|
-
},
|
|
257
|
-
...processedPrompt,
|
|
258
|
-
];
|
|
259
|
-
|
|
202
|
+
const toolSystemPrompt = processedPrompt[0].role === "system" ? [
|
|
203
|
+
{
|
|
204
|
+
role: "system",
|
|
205
|
+
content: HermesPrompt + "\n\n" + processedPrompt[0].content
|
|
206
|
+
},
|
|
207
|
+
...processedPrompt.slice(1)
|
|
208
|
+
] : [
|
|
209
|
+
{
|
|
210
|
+
role: "system",
|
|
211
|
+
content: HermesPrompt
|
|
212
|
+
},
|
|
213
|
+
...processedPrompt
|
|
214
|
+
];
|
|
260
215
|
return {
|
|
261
216
|
...params,
|
|
262
217
|
mode: {
|
|
263
218
|
// set the mode back to regular and remove the default tools.
|
|
264
|
-
type: "regular"
|
|
219
|
+
type: "regular"
|
|
265
220
|
},
|
|
266
|
-
prompt: toolSystemPrompt
|
|
221
|
+
prompt: toolSystemPrompt
|
|
267
222
|
};
|
|
268
|
-
}
|
|
223
|
+
}
|
|
269
224
|
};
|
|
270
225
|
}
|
|
226
|
+
|
|
227
|
+
// src/index.ts
|
|
228
|
+
var gemmaToolMiddleware = createToolMiddleware({
|
|
229
|
+
toolSystemPromptTemplate(tools) {
|
|
230
|
+
return `You have access to functions. If you decide to invoke any of the function(s),
|
|
231
|
+
you MUST put it in the format of
|
|
232
|
+
\`\`\`tool_call
|
|
233
|
+
{'name': <function-name>, 'arguments': <args-dict>}
|
|
234
|
+
\`\`\`
|
|
235
|
+
You SHOULD NOT include any other text in the response if you call a function
|
|
236
|
+
${tools}`;
|
|
237
|
+
},
|
|
238
|
+
toolCallTag: "```tool_call\n",
|
|
239
|
+
toolCallEndTag: "```",
|
|
240
|
+
toolResponseTag: "```tool_response\n",
|
|
241
|
+
toolResponseEndTag: "\n```"
|
|
242
|
+
});
|
|
243
|
+
var hermesToolMiddleware = createToolMiddleware({});
|
|
244
|
+
export {
|
|
245
|
+
createToolMiddleware,
|
|
246
|
+
defaultTemplate,
|
|
247
|
+
gemmaToolMiddleware,
|
|
248
|
+
hermesToolMiddleware
|
|
249
|
+
};
|
|
250
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tool-call-middleware.ts","../src/utils.ts","../src/index.ts"],"sourcesContent":["import {\n generateId,\n LanguageModelV1Middleware,\n LanguageModelV1Prompt,\n LanguageModelV1StreamPart,\n} from \"ai\";\nimport * as RJSON from \"relaxed-json\";\nimport { getPotentialStartIndex } from \"./utils\";\n\nexport const defaultTemplate = (tools: string) =>\n `You are a function calling AI model.\nYou are provided with function signatures within <tools></tools> XML tags.\nYou may call one or more functions to assist with the user query.\nDon't make assumptions about what values to plug into functions.\nHere are the available tools: <tools>${tools}</tools>\nUse the following pydantic model json schema for each tool call you will make: {'title': 'FunctionCall', 'type': 'object', 'properties': {'arguments': {'title': 'Arguments', 'type': 'object'}, 'name': {'title': 'Name', 'type': 'string'}}, 'required': ['arguments', 'name']}\nFor each function call return a json object with function name and arguments within <tool_call></tool_call> XML tags as follows:\n<tool_call>\n{'arguments': <args-dict>, 'name': <function-name>}\n</tool_call>`;\n\nexport function createToolMiddleware({\n toolCallTag = \"<tool_call>\",\n toolCallEndTag = \"</tool_call>\",\n toolResponseTag = \"<tool_response>\",\n toolResponseEndTag = \"</tool_response>\",\n toolSystemPromptTemplate = defaultTemplate,\n}: {\n toolCallTag?: string;\n toolCallEndTag?: string;\n toolResponseTag?: string;\n toolResponseEndTag?: string;\n toolSystemPromptTemplate?: (tools: string) => string;\n}): LanguageModelV1Middleware {\n return {\n middlewareVersion: \"v1\",\n wrapStream: async ({ doStream }) => {\n const { stream, ...rest } = await doStream();\n\n let isFirstToolCall = true;\n let isFirstText = true;\n let afterSwitch = false;\n let isToolCall = false;\n let buffer = \"\";\n\n let toolCallIndex = -1;\n let toolCallBuffer: string[] = [];\n\n const transformStream = new TransformStream<\n LanguageModelV1StreamPart,\n LanguageModelV1StreamPart\n >({\n transform(chunk, controller) {\n if (chunk.type === \"finish\") {\n if (toolCallBuffer.length > 0) {\n toolCallBuffer.forEach((toolCall) => {\n try {\n const parsedToolCall = RJSON.parse(toolCall) as {\n name: string;\n arguments: string;\n };\n\n controller.enqueue({\n type: \"tool-call\",\n toolCallType: \"function\",\n toolCallId: generateId(),\n toolName: parsedToolCall.name,\n args: JSON.stringify(parsedToolCall.arguments),\n });\n } catch (e) {\n console.error(`Error parsing tool call: ${toolCall}`, e);\n\n controller.enqueue({\n type: \"text-delta\",\n textDelta: `Failed to parse tool call: ${e}`,\n });\n }\n });\n }\n\n // stop token\n controller.enqueue(chunk);\n\n return;\n } else if (chunk.type !== \"text-delta\") {\n controller.enqueue(chunk);\n return;\n }\n\n buffer += chunk.textDelta;\n\n function publish(text: string) {\n if (text.length > 0) {\n const prefix =\n afterSwitch && (isToolCall ? !isFirstToolCall : !isFirstText)\n ? \"\\n\" // separator\n : \"\";\n\n if (isToolCall) {\n if (!toolCallBuffer[toolCallIndex]) {\n toolCallBuffer[toolCallIndex] = \"\";\n }\n\n toolCallBuffer[toolCallIndex] += text;\n } else {\n controller.enqueue({\n type: \"text-delta\",\n textDelta: prefix + text,\n });\n }\n\n afterSwitch = false;\n\n if (isToolCall) {\n isFirstToolCall = false;\n } else {\n isFirstText = false;\n }\n }\n }\n\n do {\n const nextTag = isToolCall ? toolCallEndTag : toolCallTag;\n const startIndex = getPotentialStartIndex(buffer, nextTag);\n\n // no opening or closing tag found, publish the buffer\n if (startIndex == null) {\n publish(buffer);\n buffer = \"\";\n break;\n }\n\n // publish text before the tag\n publish(buffer.slice(0, startIndex));\n\n const foundFullMatch = startIndex + nextTag.length <= buffer.length;\n\n if (foundFullMatch) {\n buffer = buffer.slice(startIndex + nextTag.length);\n toolCallIndex++;\n isToolCall = !isToolCall;\n afterSwitch = true;\n } else {\n buffer = buffer.slice(startIndex);\n break;\n }\n } while (true);\n },\n });\n\n return {\n stream: stream.pipeThrough(transformStream),\n ...rest,\n };\n },\n wrapGenerate: async ({ doGenerate }) => {\n const result = await doGenerate();\n\n if (!result.text?.includes(toolCallTag)) {\n return result;\n }\n\n const toolCallRegex = new RegExp(\n `${toolCallTag}(.*?)(?:${toolCallEndTag}|$)`,\n \"gs\"\n );\n const matches = [...result.text.matchAll(toolCallRegex)];\n const function_call_tuples = matches.map((match) => match[1] || match[2]);\n\n return {\n ...result,\n // TODO: Return the remaining value after extracting the tool call tag.\n text: \"\",\n toolCalls: function_call_tuples.map((toolCall) => {\n const parsedToolCall = RJSON.parse(toolCall) as {\n name: string;\n arguments: string;\n };\n\n const toolName = parsedToolCall.name;\n const args = parsedToolCall.arguments;\n\n return {\n toolCallType: \"function\",\n toolCallId: generateId(),\n toolName: toolName,\n args: RJSON.stringify(args),\n };\n }),\n };\n },\n\n transformParams: async ({ params }) => {\n const processedPrompt = params.prompt.map((message) => {\n if (message.role === \"assistant\") {\n return {\n role: \"assistant\",\n content: message.content.map((content) => {\n if (content.type === \"tool-call\") {\n return {\n type: \"text\",\n text: `${toolCallTag}${JSON.stringify({\n arguments: content.args,\n name: content.toolName,\n })}${toolCallEndTag}`,\n };\n }\n\n return content;\n }),\n };\n } else if (message.role === \"tool\") {\n return {\n role: \"user\",\n content: [\n {\n type: \"text\",\n text: message.content\n .map(\n (content) =>\n `${toolResponseTag}${JSON.stringify({\n toolName: content.toolName,\n result: content.result,\n })}${toolResponseEndTag}`\n )\n .join(\"\\n\"),\n },\n ],\n };\n }\n\n return message;\n }) as LanguageModelV1Prompt;\n\n // Appropriate fixes are needed as they are disappearing in LanguageModelV2\n const originalToolDefinitions =\n params.mode.type === \"regular\" && params.mode.tools\n ? params.mode.tools\n : {};\n\n const HermesPrompt = toolSystemPromptTemplate(\n JSON.stringify(Object.entries(originalToolDefinitions))\n );\n\n const toolSystemPrompt: LanguageModelV1Prompt =\n processedPrompt[0].role === \"system\"\n ? [\n {\n role: \"system\",\n content: HermesPrompt + \"\\n\\n\" + processedPrompt[0].content,\n },\n ...processedPrompt.slice(1),\n ]\n : [\n {\n role: \"system\",\n content: HermesPrompt,\n },\n ...processedPrompt,\n ];\n\n return {\n ...params,\n mode: {\n // set the mode back to regular and remove the default tools.\n type: \"regular\",\n },\n prompt: toolSystemPrompt,\n };\n },\n };\n}\n","/**\n * Returns the index of the start of the searchedText in the text, or null if it\n * is not found.\n * ref: https://github.com/vercel/ai/blob/452bf12f0be9cb398d4af85a006bca13c8ce36d8/packages/ai/core/util/get-potential-start-index.ts\n */\nexport function getPotentialStartIndex(\n text: string,\n searchedText: string\n): number | null {\n // Return null immediately if searchedText is empty.\n if (searchedText.length === 0) {\n return null;\n }\n\n // Check if the searchedText exists as a direct substring of text.\n const directIndex = text.indexOf(searchedText);\n if (directIndex !== -1) {\n return directIndex;\n }\n\n // Otherwise, look for the largest suffix of \"text\" that matches\n // a prefix of \"searchedText\". We go from the end of text inward.\n for (let i = text.length - 1; i >= 0; i--) {\n const suffix = text.substring(i);\n if (searchedText.startsWith(suffix)) {\n return i;\n }\n }\n\n return null;\n}\n","import { defaultTemplate, createToolMiddleware } from \"./tool-call-middleware\";\n\nconst gemmaToolMiddleware = createToolMiddleware({\n toolSystemPromptTemplate(tools) {\n return `You have access to functions. If you decide to invoke any of the function(s),\n you MUST put it in the format of\n \\`\\`\\`tool_call\n {'name': <function-name>, 'arguments': <args-dict>}\n \\`\\`\\`\n You SHOULD NOT include any other text in the response if you call a function\n ${tools}`;\n },\n toolCallTag: \"```tool_call\\n\",\n toolCallEndTag: \"```\",\n toolResponseTag: \"```tool_response\\n\",\n toolResponseEndTag: \"\\n```\",\n});\n\nconst hermesToolMiddleware = createToolMiddleware({});\n\nexport {\n defaultTemplate,\n gemmaToolMiddleware,\n hermesToolMiddleware,\n createToolMiddleware,\n};\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,23 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk-tool/parser",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"
|
|
7
|
-
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist/**/*"
|
|
10
|
+
],
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
8
13
|
},
|
|
9
14
|
"keywords": [],
|
|
10
15
|
"author": "",
|
|
11
16
|
"license": "ISC",
|
|
12
|
-
"packageManager": "pnpm@9.14.4+sha1.64b6e81e79630419b675c555ef3b65607cfd6315",
|
|
13
17
|
"dependencies": {
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"relaxed-json": "^1.0.3",
|
|
17
|
-
"tsx": "^4.19.3",
|
|
18
|
-
"zod": "^3.24.2"
|
|
18
|
+
"ai": "^4.3.9",
|
|
19
|
+
"relaxed-json": "^1.0.3"
|
|
19
20
|
},
|
|
20
21
|
"devDependencies": {
|
|
21
|
-
"@types/node": "^22.14.
|
|
22
|
+
"@types/node": "^22.14.1",
|
|
23
|
+
"@types/relaxed-json": "^1.0.4",
|
|
24
|
+
"tsup": "^8.4.0"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsup",
|
|
28
|
+
"dev": "tsup --watch"
|
|
22
29
|
}
|
|
23
30
|
}
|
package/src/index.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
|
2
|
-
import { generateText, streamText, wrapLanguageModel } from "ai";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { hermesToolMiddleware } from "./hermes-middleware";
|
|
5
|
-
|
|
6
|
-
const openrouter = createOpenAICompatible({
|
|
7
|
-
name: "openrouter",
|
|
8
|
-
apiKey: process.env.OPENROUTER_API_KEY,
|
|
9
|
-
baseURL: "https://openrouter.ai/api/v1",
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
async function main() {
|
|
13
|
-
const result = streamText({
|
|
14
|
-
// model: openrouter("openai/gpt-4o"),
|
|
15
|
-
model: wrapLanguageModel({
|
|
16
|
-
model: openrouter("google/gemma-3-27b-it"),
|
|
17
|
-
// model: openrouter("nousresearch/hermes-3-llama-3.1-70b"),
|
|
18
|
-
middleware: hermesToolMiddleware({
|
|
19
|
-
toolSystemPromptTemplate(tools) {
|
|
20
|
-
return `You have access to functions. If you decide to invoke any of the function(s),
|
|
21
|
-
you MUST put it in the format of
|
|
22
|
-
\`\`\`tool_call
|
|
23
|
-
{'name': <function-name>, 'arguments': <args-dict>}
|
|
24
|
-
\`\`\`
|
|
25
|
-
You SHOULD NOT include any other text in the response if you call a function
|
|
26
|
-
${tools}`;
|
|
27
|
-
},
|
|
28
|
-
toolCallTag: "```tool_call\n",
|
|
29
|
-
toolCallEndTag: "```",
|
|
30
|
-
toolResponseTag: "```tool_response\n",
|
|
31
|
-
toolResponseEndTag: "\n```",
|
|
32
|
-
}),
|
|
33
|
-
}),
|
|
34
|
-
system: "You are a helpful assistant.",
|
|
35
|
-
// prompt: "What is the weather in New York and Los Angeles?",
|
|
36
|
-
prompt: "What is the weather in my city?",
|
|
37
|
-
maxSteps: 4,
|
|
38
|
-
tools: {
|
|
39
|
-
get_location: {
|
|
40
|
-
description: "Get the User's location.",
|
|
41
|
-
parameters: z.object({}),
|
|
42
|
-
execute: async () => {
|
|
43
|
-
// Simulate a location API call
|
|
44
|
-
return {
|
|
45
|
-
city: "New York",
|
|
46
|
-
country: "USA",
|
|
47
|
-
};
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
get_weather: {
|
|
51
|
-
description:
|
|
52
|
-
"Get the weather for a given city. " +
|
|
53
|
-
"Example cities: 'New York', 'Los Angeles', 'Paris'.",
|
|
54
|
-
parameters: z.object({ city: z.string() }),
|
|
55
|
-
execute: async ({ city }) => {
|
|
56
|
-
// Simulate a weather API call
|
|
57
|
-
const temperature = Math.floor(Math.random() * 100);
|
|
58
|
-
return {
|
|
59
|
-
city,
|
|
60
|
-
temperature,
|
|
61
|
-
condition: "sunny",
|
|
62
|
-
};
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
for await (const part of result.fullStream) {
|
|
69
|
-
if (part.type === "text-delta") {
|
|
70
|
-
process.stdout.write(part.textDelta);
|
|
71
|
-
} else if (part.type === "tool-result") {
|
|
72
|
-
console.log({
|
|
73
|
-
name: part.toolName,
|
|
74
|
-
args: part.args,
|
|
75
|
-
result: part.result,
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
console.log("\n\n[done]");
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
main().catch(console.error);
|
package/src/utils.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Returns the index of the start of the searchedText in the text, or null if it
|
|
3
|
-
* is not found.
|
|
4
|
-
* ref: https://github.com/vercel/ai/blob/452bf12f0be9cb398d4af85a006bca13c8ce36d8/packages/ai/core/util/get-potential-start-index.ts
|
|
5
|
-
*/
|
|
6
|
-
export function getPotentialStartIndex(
|
|
7
|
-
text: string,
|
|
8
|
-
searchedText: string
|
|
9
|
-
): number | null {
|
|
10
|
-
// Return null immediately if searchedText is empty.
|
|
11
|
-
if (searchedText.length === 0) {
|
|
12
|
-
return null;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// Check if the searchedText exists as a direct substring of text.
|
|
16
|
-
const directIndex = text.indexOf(searchedText);
|
|
17
|
-
if (directIndex !== -1) {
|
|
18
|
-
return directIndex;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Otherwise, look for the largest suffix of "text" that matches
|
|
22
|
-
// a prefix of "searchedText". We go from the end of text inward.
|
|
23
|
-
for (let i = text.length - 1; i >= 0; i--) {
|
|
24
|
-
const suffix = text.substring(i);
|
|
25
|
-
if (searchedText.startsWith(suffix)) {
|
|
26
|
-
return i;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return null;
|
|
31
|
-
}
|