@nodetool-ai/agents 0.7.0-rc.11
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/agent-executor.d.ts +62 -0
- package/dist/agent-executor.d.ts.map +1 -0
- package/dist/agent-executor.js +324 -0
- package/dist/agent-executor.js.map +1 -0
- package/dist/agent-step-executor.d.ts +28 -0
- package/dist/agent-step-executor.d.ts.map +1 -0
- package/dist/agent-step-executor.js +99 -0
- package/dist/agent-step-executor.js.map +1 -0
- package/dist/agent-workflow-runner.d.ts +31 -0
- package/dist/agent-workflow-runner.d.ts.map +1 -0
- package/dist/agent-workflow-runner.js +124 -0
- package/dist/agent-workflow-runner.js.map +1 -0
- package/dist/agent.d.ts +115 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +587 -0
- package/dist/agent.js.map +1 -0
- package/dist/base-agent.d.ts +42 -0
- package/dist/base-agent.d.ts.map +1 -0
- package/dist/base-agent.js +29 -0
- package/dist/base-agent.js.map +1 -0
- package/dist/constants.d.ts +8 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +8 -0
- package/dist/constants.js.map +1 -0
- package/dist/graph-builder.d.ts +49 -0
- package/dist/graph-builder.d.ts.map +1 -0
- package/dist/graph-builder.js +206 -0
- package/dist/graph-builder.js.map +1 -0
- package/dist/graph-planner.d.ts +56 -0
- package/dist/graph-planner.d.ts.map +1 -0
- package/dist/graph-planner.js +311 -0
- package/dist/graph-planner.js.map +1 -0
- package/dist/index.d.ts +79 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +81 -0
- package/dist/index.js.map +1 -0
- package/dist/js-sandbox.d.ts +84 -0
- package/dist/js-sandbox.d.ts.map +1 -0
- package/dist/js-sandbox.js +654 -0
- package/dist/js-sandbox.js.map +1 -0
- package/dist/multi-mode-agent.d.ts +86 -0
- package/dist/multi-mode-agent.d.ts.map +1 -0
- package/dist/multi-mode-agent.js +476 -0
- package/dist/multi-mode-agent.js.map +1 -0
- package/dist/output-format.d.ts +10 -0
- package/dist/output-format.d.ts.map +1 -0
- package/dist/output-format.js +17 -0
- package/dist/output-format.js.map +1 -0
- package/dist/parallel-task-executor.d.ts +87 -0
- package/dist/parallel-task-executor.d.ts.map +1 -0
- package/dist/parallel-task-executor.js +332 -0
- package/dist/parallel-task-executor.js.map +1 -0
- package/dist/prompts/graph-planner-prompt.d.ts +48 -0
- package/dist/prompts/graph-planner-prompt.d.ts.map +1 -0
- package/dist/prompts/graph-planner-prompt.js +180 -0
- package/dist/prompts/graph-planner-prompt.js.map +1 -0
- package/dist/reject-agentic-provider.d.ts +21 -0
- package/dist/reject-agentic-provider.d.ts.map +1 -0
- package/dist/reject-agentic-provider.js +32 -0
- package/dist/reject-agentic-provider.js.map +1 -0
- package/dist/simple-agent.d.ts +29 -0
- package/dist/simple-agent.d.ts.map +1 -0
- package/dist/simple-agent.js +54 -0
- package/dist/simple-agent.js.map +1 -0
- package/dist/step-executor.d.ts +162 -0
- package/dist/step-executor.d.ts.map +1 -0
- package/dist/step-executor.js +1114 -0
- package/dist/step-executor.js.map +1 -0
- package/dist/sub-agent-planner.d.ts +33 -0
- package/dist/sub-agent-planner.d.ts.map +1 -0
- package/dist/sub-agent-planner.js +203 -0
- package/dist/sub-agent-planner.js.map +1 -0
- package/dist/task-executor.d.ts +89 -0
- package/dist/task-executor.d.ts.map +1 -0
- package/dist/task-executor.js +359 -0
- package/dist/task-executor.js.map +1 -0
- package/dist/task-planner.d.ts +44 -0
- package/dist/task-planner.d.ts.map +1 -0
- package/dist/task-planner.js +532 -0
- package/dist/task-planner.js.map +1 -0
- package/dist/team/db-task-board.d.ts +54 -0
- package/dist/team/db-task-board.d.ts.map +1 -0
- package/dist/team/db-task-board.js +361 -0
- package/dist/team/db-task-board.js.map +1 -0
- package/dist/team/edge-message-bus.d.ts +51 -0
- package/dist/team/edge-message-bus.d.ts.map +1 -0
- package/dist/team/edge-message-bus.js +169 -0
- package/dist/team/edge-message-bus.js.map +1 -0
- package/dist/team/edge-task-board.d.ts +77 -0
- package/dist/team/edge-task-board.d.ts.map +1 -0
- package/dist/team/edge-task-board.js +140 -0
- package/dist/team/edge-task-board.js.map +1 -0
- package/dist/team/index.d.ts +16 -0
- package/dist/team/index.d.ts.map +1 -0
- package/dist/team/index.js +17 -0
- package/dist/team/index.js.map +1 -0
- package/dist/team/message-bus.d.ts +58 -0
- package/dist/team/message-bus.d.ts.map +1 -0
- package/dist/team/message-bus.js +151 -0
- package/dist/team/message-bus.js.map +1 -0
- package/dist/team/task-board.d.ts +106 -0
- package/dist/team/task-board.d.ts.map +1 -0
- package/dist/team/task-board.js +370 -0
- package/dist/team/task-board.js.map +1 -0
- package/dist/team/team-executor.d.ts +72 -0
- package/dist/team/team-executor.d.ts.map +1 -0
- package/dist/team/team-executor.js +466 -0
- package/dist/team/team-executor.js.map +1 -0
- package/dist/team/team-tools.d.ts +252 -0
- package/dist/team/team-tools.d.ts.map +1 -0
- package/dist/team/team-tools.js +412 -0
- package/dist/team/team-tools.js.map +1 -0
- package/dist/team/types.d.ts +198 -0
- package/dist/team/types.d.ts.map +1 -0
- package/dist/team/types.js +8 -0
- package/dist/team/types.js.map +1 -0
- package/dist/tools/add-edge-tool.d.ts +18 -0
- package/dist/tools/add-edge-tool.d.ts.map +1 -0
- package/dist/tools/add-edge-tool.js +92 -0
- package/dist/tools/add-edge-tool.js.map +1 -0
- package/dist/tools/add-node-tool.d.ts +22 -0
- package/dist/tools/add-node-tool.d.ts.map +1 -0
- package/dist/tools/add-node-tool.js +128 -0
- package/dist/tools/add-node-tool.js.map +1 -0
- package/dist/tools/asset-tools.d.ts +58 -0
- package/dist/tools/asset-tools.d.ts.map +1 -0
- package/dist/tools/asset-tools.js +191 -0
- package/dist/tools/asset-tools.js.map +1 -0
- package/dist/tools/base-tool.d.ts +25 -0
- package/dist/tools/base-tool.d.ts.map +1 -0
- package/dist/tools/base-tool.js +24 -0
- package/dist/tools/base-tool.js.map +1 -0
- package/dist/tools/browser-tools.d.ts +32 -0
- package/dist/tools/browser-tools.d.ts.map +1 -0
- package/dist/tools/browser-tools.js +208 -0
- package/dist/tools/browser-tools.js.map +1 -0
- package/dist/tools/calculator-tool.d.ts +24 -0
- package/dist/tools/calculator-tool.d.ts.map +1 -0
- package/dist/tools/calculator-tool.js +64 -0
- package/dist/tools/calculator-tool.js.map +1 -0
- package/dist/tools/claude-code-tools.d.ts +96 -0
- package/dist/tools/claude-code-tools.d.ts.map +1 -0
- package/dist/tools/claude-code-tools.js +463 -0
- package/dist/tools/claude-code-tools.js.map +1 -0
- package/dist/tools/code-tools.d.ts +26 -0
- package/dist/tools/code-tools.d.ts.map +1 -0
- package/dist/tools/code-tools.js +82 -0
- package/dist/tools/code-tools.js.map +1 -0
- package/dist/tools/control-tool.d.ts +47 -0
- package/dist/tools/control-tool.d.ts.map +1 -0
- package/dist/tools/control-tool.js +101 -0
- package/dist/tools/control-tool.js.map +1 -0
- package/dist/tools/create-plan-tool.d.ts +29 -0
- package/dist/tools/create-plan-tool.d.ts.map +1 -0
- package/dist/tools/create-plan-tool.js +355 -0
- package/dist/tools/create-plan-tool.js.map +1 -0
- package/dist/tools/create-task-tool.d.ts +28 -0
- package/dist/tools/create-task-tool.d.ts.map +1 -0
- package/dist/tools/create-task-tool.js +254 -0
- package/dist/tools/create-task-tool.js.map +1 -0
- package/dist/tools/dataseo-tools.d.ts +38 -0
- package/dist/tools/dataseo-tools.d.ts.map +1 -0
- package/dist/tools/dataseo-tools.js +329 -0
- package/dist/tools/dataseo-tools.js.map +1 -0
- package/dist/tools/email-tools.d.ts +76 -0
- package/dist/tools/email-tools.d.ts.map +1 -0
- package/dist/tools/email-tools.js +259 -0
- package/dist/tools/email-tools.js.map +1 -0
- package/dist/tools/filesystem-tools.d.ts +77 -0
- package/dist/tools/filesystem-tools.d.ts.map +1 -0
- package/dist/tools/filesystem-tools.js +334 -0
- package/dist/tools/filesystem-tools.js.map +1 -0
- package/dist/tools/find-model-tool.d.ts +25 -0
- package/dist/tools/find-model-tool.d.ts.map +1 -0
- package/dist/tools/find-model-tool.js +236 -0
- package/dist/tools/find-model-tool.js.map +1 -0
- package/dist/tools/finish-graph-tool.d.ts +23 -0
- package/dist/tools/finish-graph-tool.d.ts.map +1 -0
- package/dist/tools/finish-graph-tool.js +46 -0
- package/dist/tools/finish-graph-tool.js.map +1 -0
- package/dist/tools/finish-step-tool.d.ts +16 -0
- package/dist/tools/finish-step-tool.d.ts.map +1 -0
- package/dist/tools/finish-step-tool.js +68 -0
- package/dist/tools/finish-step-tool.js.map +1 -0
- package/dist/tools/google-tools.d.ts +44 -0
- package/dist/tools/google-tools.d.ts.map +1 -0
- package/dist/tools/google-tools.js +176 -0
- package/dist/tools/google-tools.js.map +1 -0
- package/dist/tools/http-tools.d.ts +56 -0
- package/dist/tools/http-tools.d.ts.map +1 -0
- package/dist/tools/http-tools.js +180 -0
- package/dist/tools/http-tools.js.map +1 -0
- package/dist/tools/js-code-tool.d.ts +30 -0
- package/dist/tools/js-code-tool.d.ts.map +1 -0
- package/dist/tools/js-code-tool.js +98 -0
- package/dist/tools/js-code-tool.js.map +1 -0
- package/dist/tools/local-get-node-info-tool.d.ts +19 -0
- package/dist/tools/local-get-node-info-tool.d.ts.map +1 -0
- package/dist/tools/local-get-node-info-tool.js +75 -0
- package/dist/tools/local-get-node-info-tool.js.map +1 -0
- package/dist/tools/local-list-nodes-tool.d.ts +19 -0
- package/dist/tools/local-list-nodes-tool.d.ts.map +1 -0
- package/dist/tools/local-list-nodes-tool.js +69 -0
- package/dist/tools/local-list-nodes-tool.js.map +1 -0
- package/dist/tools/local-search-nodes-tool.d.ts +21 -0
- package/dist/tools/local-search-nodes-tool.d.ts.map +1 -0
- package/dist/tools/local-search-nodes-tool.js +111 -0
- package/dist/tools/local-search-nodes-tool.js.map +1 -0
- package/dist/tools/math-tools.d.ts +156 -0
- package/dist/tools/math-tools.d.ts.map +1 -0
- package/dist/tools/math-tools.js +422 -0
- package/dist/tools/math-tools.js.map +1 -0
- package/dist/tools/mcp-tools.d.ts +413 -0
- package/dist/tools/mcp-tools.d.ts.map +1 -0
- package/dist/tools/mcp-tools.js +618 -0
- package/dist/tools/mcp-tools.js.map +1 -0
- package/dist/tools/media-tools.d.ts +277 -0
- package/dist/tools/media-tools.d.ts.map +1 -0
- package/dist/tools/media-tools.js +682 -0
- package/dist/tools/media-tools.js.map +1 -0
- package/dist/tools/model-tools.d.ts +25 -0
- package/dist/tools/model-tools.d.ts.map +1 -0
- package/dist/tools/model-tools.js +48 -0
- package/dist/tools/model-tools.js.map +1 -0
- package/dist/tools/openai-tools.d.ts +73 -0
- package/dist/tools/openai-tools.d.ts.map +1 -0
- package/dist/tools/openai-tools.js +193 -0
- package/dist/tools/openai-tools.js.map +1 -0
- package/dist/tools/pdf-tools.d.ts +152 -0
- package/dist/tools/pdf-tools.d.ts.map +1 -0
- package/dist/tools/pdf-tools.js +339 -0
- package/dist/tools/pdf-tools.js.map +1 -0
- package/dist/tools/plan-builder-tools.d.ts +72 -0
- package/dist/tools/plan-builder-tools.d.ts.map +1 -0
- package/dist/tools/plan-builder-tools.js +438 -0
- package/dist/tools/plan-builder-tools.js.map +1 -0
- package/dist/tools/search-tools.d.ts +39 -0
- package/dist/tools/search-tools.d.ts.map +1 -0
- package/dist/tools/search-tools.js +218 -0
- package/dist/tools/search-tools.js.map +1 -0
- package/dist/tools/serp-providers/dataforseo-provider.d.ts +14 -0
- package/dist/tools/serp-providers/dataforseo-provider.d.ts.map +1 -0
- package/dist/tools/serp-providers/dataforseo-provider.js +114 -0
- package/dist/tools/serp-providers/dataforseo-provider.js.map +1 -0
- package/dist/tools/serp-providers/index.d.ts +35 -0
- package/dist/tools/serp-providers/index.d.ts.map +1 -0
- package/dist/tools/serp-providers/index.js +14 -0
- package/dist/tools/serp-providers/index.js.map +1 -0
- package/dist/tools/serp-providers/serpapi-provider.d.ts +18 -0
- package/dist/tools/serp-providers/serpapi-provider.d.ts.map +1 -0
- package/dist/tools/serp-providers/serpapi-provider.js +66 -0
- package/dist/tools/serp-providers/serpapi-provider.js.map +1 -0
- package/dist/tools/tool-registry.d.ts +9 -0
- package/dist/tools/tool-registry.d.ts.map +1 -0
- package/dist/tools/tool-registry.js +17 -0
- package/dist/tools/tool-registry.js.map +1 -0
- package/dist/tools/vector-tools.d.ts +224 -0
- package/dist/tools/vector-tools.d.ts.map +1 -0
- package/dist/tools/vector-tools.js +582 -0
- package/dist/tools/vector-tools.js.map +1 -0
- package/dist/tools/workspace-tools.d.ts +58 -0
- package/dist/tools/workspace-tools.d.ts.map +1 -0
- package/dist/tools/workspace-tools.js +152 -0
- package/dist/tools/workspace-tools.js.map +1 -0
- package/dist/types.d.ts +65 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/json-parser.d.ts +15 -0
- package/dist/utils/json-parser.d.ts.map +1 -0
- package/dist/utils/json-parser.js +78 -0
- package/dist/utils/json-parser.js.map +1 -0
- package/dist/utils/remove-base64-images.d.ts +12 -0
- package/dist/utils/remove-base64-images.d.ts.map +1 -0
- package/dist/utils/remove-base64-images.js +20 -0
- package/dist/utils/remove-base64-images.js.map +1 -0
- package/dist/utils/wrap-generators-parallel.d.ts +11 -0
- package/dist/utils/wrap-generators-parallel.d.ts.map +1 -0
- package/dist/utils/wrap-generators-parallel.js +38 -0
- package/dist/utils/wrap-generators-parallel.js.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1,682 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic provider-backed media generation tools for any agent loop.
|
|
3
|
+
*
|
|
4
|
+
* Each tool is a thin wrapper around `ProcessingContext.runProviderPrediction`
|
|
5
|
+
* (or `streamProviderPrediction` for capabilities that require it). They take
|
|
6
|
+
* `provider` + `model_id` + capability-specific params, dispatch to whichever
|
|
7
|
+
* `BaseProvider` is registered for that id, and return a JSON-friendly result.
|
|
8
|
+
*
|
|
9
|
+
* Persistence: outputs are saved as proper **assets** when the context
|
|
10
|
+
* exposes a `createAsset` model interface — the tool returns an
|
|
11
|
+
* `asset_id` + `asset_uri` ("asset://<id>.<ext>") that subsequent steps and
|
|
12
|
+
* the chat UI can reference. When no asset interface is wired (CLI without
|
|
13
|
+
* a model layer, tests), the tool falls back to writing the bytes to the
|
|
14
|
+
* caller-supplied `output_file` under the workspace.
|
|
15
|
+
*
|
|
16
|
+
* Pair with `find_model` so the agent first picks a `{provider, model_id}`
|
|
17
|
+
* and then calls the right generation tool.
|
|
18
|
+
*/
|
|
19
|
+
import { Buffer } from "node:buffer";
|
|
20
|
+
import { promises as fs } from "node:fs";
|
|
21
|
+
import path from "node:path";
|
|
22
|
+
import { Tool } from "./base-tool.js";
|
|
23
|
+
const MAX_INLINE_TEXT_PREVIEW = 500;
|
|
24
|
+
const MIME_TO_EXT = {
|
|
25
|
+
"image/png": "png",
|
|
26
|
+
"image/jpeg": "jpg",
|
|
27
|
+
"image/jpg": "jpg",
|
|
28
|
+
"image/webp": "webp",
|
|
29
|
+
"image/gif": "gif",
|
|
30
|
+
"video/mp4": "mp4",
|
|
31
|
+
"video/webm": "webm",
|
|
32
|
+
"audio/mpeg": "mp3",
|
|
33
|
+
"audio/mp3": "mp3",
|
|
34
|
+
"audio/wav": "wav",
|
|
35
|
+
"audio/x-wav": "wav",
|
|
36
|
+
"audio/flac": "flac",
|
|
37
|
+
"audio/ogg": "ogg",
|
|
38
|
+
"application/octet-stream": "bin"
|
|
39
|
+
};
|
|
40
|
+
function workspaceDir(context) {
|
|
41
|
+
const ws = context["workspaceDir"];
|
|
42
|
+
return typeof ws === "string" && ws ? ws : null;
|
|
43
|
+
}
|
|
44
|
+
function timestampedName(prefix, ext) {
|
|
45
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
46
|
+
return `${prefix}-${ts}.${ext}`;
|
|
47
|
+
}
|
|
48
|
+
function inferImageMime(bytes) {
|
|
49
|
+
if (bytes.length >= 8 &&
|
|
50
|
+
bytes[0] === 0x89 && bytes[1] === 0x50 &&
|
|
51
|
+
bytes[2] === 0x4e && bytes[3] === 0x47)
|
|
52
|
+
return "image/png";
|
|
53
|
+
if (bytes.length >= 3 &&
|
|
54
|
+
bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff)
|
|
55
|
+
return "image/jpeg";
|
|
56
|
+
if (bytes.length >= 6 &&
|
|
57
|
+
bytes[0] === 0x47 && bytes[1] === 0x49 && bytes[2] === 0x46)
|
|
58
|
+
return "image/gif";
|
|
59
|
+
if (bytes.length >= 12 &&
|
|
60
|
+
bytes[8] === 0x57 && bytes[9] === 0x45 &&
|
|
61
|
+
bytes[10] === 0x42 && bytes[11] === 0x50)
|
|
62
|
+
return "image/webp";
|
|
63
|
+
return "image/png";
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Persist generated bytes. Prefers `context.createAsset` (writes to storage
|
|
67
|
+
* + DB), falling back to the workspace filesystem when the model interface
|
|
68
|
+
* isn't wired. Returns whichever location is available so the agent can
|
|
69
|
+
* reference the output in subsequent steps.
|
|
70
|
+
*/
|
|
71
|
+
async function persistOutput(context, bytes, opts) {
|
|
72
|
+
const ext = MIME_TO_EXT[opts.mime] ?? "bin";
|
|
73
|
+
const result = { bytes: bytes.length, mime_type: opts.mime };
|
|
74
|
+
// Asset path: persisted in storage + DB, returns asset:// URI.
|
|
75
|
+
if (typeof context.createAsset === "function") {
|
|
76
|
+
try {
|
|
77
|
+
const name = opts.outputFile ?? timestampedName(opts.namePrefix, ext);
|
|
78
|
+
const asset = (await context.createAsset({
|
|
79
|
+
name,
|
|
80
|
+
contentType: opts.mime,
|
|
81
|
+
content: bytes
|
|
82
|
+
}));
|
|
83
|
+
if (asset && typeof asset.id === "string") {
|
|
84
|
+
result.asset_id = asset.id;
|
|
85
|
+
result.asset_uri = `asset://${asset.id}.${ext}`;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// Fall through to filesystem fallback.
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// Workspace fallback. Always write when the caller supplied output_file
|
|
93
|
+
// OR when asset creation didn't happen, so the agent has a concrete
|
|
94
|
+
// location to reference.
|
|
95
|
+
if (opts.outputFile || !result.asset_id) {
|
|
96
|
+
const fileName = opts.outputFile ?? timestampedName(opts.namePrefix, ext);
|
|
97
|
+
const ws = workspaceDir(context);
|
|
98
|
+
const filePath = ws ? path.join(ws, fileName) : fileName;
|
|
99
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
100
|
+
await fs.writeFile(filePath, Buffer.from(bytes));
|
|
101
|
+
result.path = filePath;
|
|
102
|
+
}
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
async function readWorkspaceOrAssetFile(context, inputFile) {
|
|
106
|
+
const ws = workspaceDir(context);
|
|
107
|
+
const filePath = ws ? path.join(ws, inputFile) : inputFile;
|
|
108
|
+
const buf = await fs.readFile(filePath);
|
|
109
|
+
return new Uint8Array(buf);
|
|
110
|
+
}
|
|
111
|
+
function parseModelArgs(params) {
|
|
112
|
+
const provider = params["provider"];
|
|
113
|
+
const model = params["model"];
|
|
114
|
+
if (typeof provider !== "string" || !provider) {
|
|
115
|
+
return { error: "provider must be a non-empty string (use find_model)" };
|
|
116
|
+
}
|
|
117
|
+
if (typeof model !== "string" || !model) {
|
|
118
|
+
return { error: "model must be a non-empty string (use find_model)" };
|
|
119
|
+
}
|
|
120
|
+
return { provider, model };
|
|
121
|
+
}
|
|
122
|
+
/* ------------------------------------------------------------------ */
|
|
123
|
+
/* Image generation */
|
|
124
|
+
/* ------------------------------------------------------------------ */
|
|
125
|
+
export class GenerateImageTool extends Tool {
|
|
126
|
+
name = "generate_image";
|
|
127
|
+
description = "Generate an image from a text prompt using a provider+model selected via find_model (capability=text_to_image). The result is saved as an asset (asset:// URI returned); pass `output_file` to also write a workspace copy.";
|
|
128
|
+
inputSchema = {
|
|
129
|
+
type: "object",
|
|
130
|
+
properties: {
|
|
131
|
+
provider: { type: "string", description: "Provider id from find_model." },
|
|
132
|
+
model: { type: "string", description: "Model id from find_model." },
|
|
133
|
+
prompt: { type: "string", description: "Text prompt." },
|
|
134
|
+
output_file: {
|
|
135
|
+
type: "string",
|
|
136
|
+
description: "Optional workspace-relative path to also write the result. Omit to rely on the asset URI."
|
|
137
|
+
},
|
|
138
|
+
negative_prompt: { type: "string" },
|
|
139
|
+
width: { type: "number" },
|
|
140
|
+
height: { type: "number" },
|
|
141
|
+
quality: { type: "string" }
|
|
142
|
+
},
|
|
143
|
+
required: ["provider", "model", "prompt"]
|
|
144
|
+
};
|
|
145
|
+
async process(context, params) {
|
|
146
|
+
const m = parseModelArgs(params);
|
|
147
|
+
if ("error" in m)
|
|
148
|
+
return m;
|
|
149
|
+
const prompt = params["prompt"];
|
|
150
|
+
if (typeof prompt !== "string" || !prompt)
|
|
151
|
+
return { error: "prompt is required" };
|
|
152
|
+
try {
|
|
153
|
+
const result = (await context.runProviderPrediction({
|
|
154
|
+
provider: m.provider,
|
|
155
|
+
capability: "text_to_image",
|
|
156
|
+
model: m.model,
|
|
157
|
+
params: {
|
|
158
|
+
prompt,
|
|
159
|
+
negative_prompt: params["negative_prompt"],
|
|
160
|
+
width: params["width"],
|
|
161
|
+
height: params["height"],
|
|
162
|
+
quality: params["quality"]
|
|
163
|
+
}
|
|
164
|
+
}));
|
|
165
|
+
const persisted = await persistOutput(context, result, {
|
|
166
|
+
namePrefix: "generated-image",
|
|
167
|
+
mime: inferImageMime(result),
|
|
168
|
+
outputFile: typeof params["output_file"] === "string"
|
|
169
|
+
? params["output_file"]
|
|
170
|
+
: undefined
|
|
171
|
+
});
|
|
172
|
+
return { type: "image", provider: m.provider, model: m.model, ...persisted };
|
|
173
|
+
}
|
|
174
|
+
catch (e) {
|
|
175
|
+
return { error: `text_to_image failed: ${e instanceof Error ? e.message : String(e)}` };
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
userMessage(params) {
|
|
179
|
+
return `Generating image with ${String(params["provider"])}:${String(params["model"])}`;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
export class EditImageTool extends Tool {
|
|
183
|
+
name = "edit_image";
|
|
184
|
+
description = "Transform a source image with a text prompt using a provider+model selected via find_model (capability=image_to_image). Source can be an asset URI (asset://...) or a workspace path. Result is saved as an asset.";
|
|
185
|
+
inputSchema = {
|
|
186
|
+
type: "object",
|
|
187
|
+
properties: {
|
|
188
|
+
provider: { type: "string", description: "Provider id from find_model." },
|
|
189
|
+
model: { type: "string", description: "Model id from find_model." },
|
|
190
|
+
input_file: {
|
|
191
|
+
type: "string",
|
|
192
|
+
description: "Workspace-relative path of the source image (or asset:// URI)."
|
|
193
|
+
},
|
|
194
|
+
prompt: { type: "string", description: "Text prompt describing the desired transformation." },
|
|
195
|
+
output_file: {
|
|
196
|
+
type: "string",
|
|
197
|
+
description: "Optional workspace-relative path to also write the result."
|
|
198
|
+
},
|
|
199
|
+
negative_prompt: { type: "string" },
|
|
200
|
+
target_width: { type: "number" },
|
|
201
|
+
target_height: { type: "number" },
|
|
202
|
+
strength: { type: "number" }
|
|
203
|
+
},
|
|
204
|
+
required: ["provider", "model", "input_file", "prompt"]
|
|
205
|
+
};
|
|
206
|
+
async process(context, params) {
|
|
207
|
+
const m = parseModelArgs(params);
|
|
208
|
+
if ("error" in m)
|
|
209
|
+
return m;
|
|
210
|
+
const inputFile = params["input_file"];
|
|
211
|
+
const prompt = params["prompt"];
|
|
212
|
+
if (typeof inputFile !== "string" || !inputFile)
|
|
213
|
+
return { error: "input_file is required" };
|
|
214
|
+
if (typeof prompt !== "string" || !prompt)
|
|
215
|
+
return { error: "prompt is required" };
|
|
216
|
+
try {
|
|
217
|
+
const image = await readWorkspaceOrAssetFile(context, inputFile);
|
|
218
|
+
const result = (await context.runProviderPrediction({
|
|
219
|
+
provider: m.provider,
|
|
220
|
+
capability: "image_to_image",
|
|
221
|
+
model: m.model,
|
|
222
|
+
params: {
|
|
223
|
+
image,
|
|
224
|
+
prompt,
|
|
225
|
+
negative_prompt: params["negative_prompt"],
|
|
226
|
+
target_width: params["target_width"],
|
|
227
|
+
target_height: params["target_height"],
|
|
228
|
+
strength: params["strength"]
|
|
229
|
+
}
|
|
230
|
+
}));
|
|
231
|
+
const persisted = await persistOutput(context, result, {
|
|
232
|
+
namePrefix: "edited-image",
|
|
233
|
+
mime: inferImageMime(result),
|
|
234
|
+
outputFile: typeof params["output_file"] === "string"
|
|
235
|
+
? params["output_file"]
|
|
236
|
+
: undefined
|
|
237
|
+
});
|
|
238
|
+
return { type: "image", provider: m.provider, model: m.model, ...persisted };
|
|
239
|
+
}
|
|
240
|
+
catch (e) {
|
|
241
|
+
return { error: `image_to_image failed: ${e instanceof Error ? e.message : String(e)}` };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
userMessage(params) {
|
|
245
|
+
return `Editing image with ${String(params["provider"])}:${String(params["model"])}`;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
/* ------------------------------------------------------------------ */
|
|
249
|
+
/* Video generation */
|
|
250
|
+
/* ------------------------------------------------------------------ */
|
|
251
|
+
export class GenerateVideoTool extends Tool {
|
|
252
|
+
name = "generate_video";
|
|
253
|
+
description = "Generate a video from a text prompt using a provider+model selected via find_model (capability=text_to_video). Result is saved as an asset (asset:// URI returned).";
|
|
254
|
+
inputSchema = {
|
|
255
|
+
type: "object",
|
|
256
|
+
properties: {
|
|
257
|
+
provider: { type: "string" },
|
|
258
|
+
model: { type: "string" },
|
|
259
|
+
prompt: { type: "string" },
|
|
260
|
+
output_file: {
|
|
261
|
+
type: "string",
|
|
262
|
+
description: "Optional workspace-relative path to also write the result."
|
|
263
|
+
},
|
|
264
|
+
negative_prompt: { type: "string" },
|
|
265
|
+
num_frames: { type: "number" },
|
|
266
|
+
aspect_ratio: { type: "string" },
|
|
267
|
+
resolution: { type: "string" }
|
|
268
|
+
},
|
|
269
|
+
required: ["provider", "model", "prompt"]
|
|
270
|
+
};
|
|
271
|
+
async process(context, params) {
|
|
272
|
+
const m = parseModelArgs(params);
|
|
273
|
+
if ("error" in m)
|
|
274
|
+
return m;
|
|
275
|
+
const prompt = params["prompt"];
|
|
276
|
+
if (typeof prompt !== "string" || !prompt)
|
|
277
|
+
return { error: "prompt is required" };
|
|
278
|
+
try {
|
|
279
|
+
const result = (await context.runProviderPrediction({
|
|
280
|
+
provider: m.provider,
|
|
281
|
+
capability: "text_to_video",
|
|
282
|
+
model: m.model,
|
|
283
|
+
params: {
|
|
284
|
+
prompt,
|
|
285
|
+
negative_prompt: params["negative_prompt"],
|
|
286
|
+
num_frames: params["num_frames"],
|
|
287
|
+
aspect_ratio: params["aspect_ratio"],
|
|
288
|
+
resolution: params["resolution"]
|
|
289
|
+
}
|
|
290
|
+
}));
|
|
291
|
+
const persisted = await persistOutput(context, result, {
|
|
292
|
+
namePrefix: "generated-video",
|
|
293
|
+
mime: "video/mp4",
|
|
294
|
+
outputFile: typeof params["output_file"] === "string"
|
|
295
|
+
? params["output_file"]
|
|
296
|
+
: undefined
|
|
297
|
+
});
|
|
298
|
+
return { type: "video", provider: m.provider, model: m.model, ...persisted };
|
|
299
|
+
}
|
|
300
|
+
catch (e) {
|
|
301
|
+
return { error: `text_to_video failed: ${e instanceof Error ? e.message : String(e)}` };
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
userMessage(params) {
|
|
305
|
+
return `Generating video with ${String(params["provider"])}:${String(params["model"])}`;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
export class AnimateImageTool extends Tool {
|
|
309
|
+
name = "animate_image";
|
|
310
|
+
description = "Animate a source image into a video using a provider+model selected via find_model (capability=image_to_video). Source can be a workspace path or asset URI; result is saved as an asset.";
|
|
311
|
+
inputSchema = {
|
|
312
|
+
type: "object",
|
|
313
|
+
properties: {
|
|
314
|
+
provider: { type: "string" },
|
|
315
|
+
model: { type: "string" },
|
|
316
|
+
input_file: {
|
|
317
|
+
type: "string",
|
|
318
|
+
description: "Workspace-relative path of the source image (or asset:// URI)."
|
|
319
|
+
},
|
|
320
|
+
output_file: {
|
|
321
|
+
type: "string",
|
|
322
|
+
description: "Optional workspace-relative path to also write the result."
|
|
323
|
+
},
|
|
324
|
+
prompt: { type: "string" },
|
|
325
|
+
num_frames: { type: "number" },
|
|
326
|
+
aspect_ratio: { type: "string" },
|
|
327
|
+
resolution: { type: "string" }
|
|
328
|
+
},
|
|
329
|
+
required: ["provider", "model", "input_file"]
|
|
330
|
+
};
|
|
331
|
+
async process(context, params) {
|
|
332
|
+
const m = parseModelArgs(params);
|
|
333
|
+
if ("error" in m)
|
|
334
|
+
return m;
|
|
335
|
+
const inputFile = params["input_file"];
|
|
336
|
+
if (typeof inputFile !== "string" || !inputFile)
|
|
337
|
+
return { error: "input_file is required" };
|
|
338
|
+
try {
|
|
339
|
+
const image = await readWorkspaceOrAssetFile(context, inputFile);
|
|
340
|
+
const result = (await context.runProviderPrediction({
|
|
341
|
+
provider: m.provider,
|
|
342
|
+
capability: "image_to_video",
|
|
343
|
+
model: m.model,
|
|
344
|
+
params: {
|
|
345
|
+
image,
|
|
346
|
+
prompt: params["prompt"],
|
|
347
|
+
num_frames: params["num_frames"],
|
|
348
|
+
aspect_ratio: params["aspect_ratio"],
|
|
349
|
+
resolution: params["resolution"]
|
|
350
|
+
}
|
|
351
|
+
}));
|
|
352
|
+
const persisted = await persistOutput(context, result, {
|
|
353
|
+
namePrefix: "animated-video",
|
|
354
|
+
mime: "video/mp4",
|
|
355
|
+
outputFile: typeof params["output_file"] === "string"
|
|
356
|
+
? params["output_file"]
|
|
357
|
+
: undefined
|
|
358
|
+
});
|
|
359
|
+
return { type: "video", provider: m.provider, model: m.model, ...persisted };
|
|
360
|
+
}
|
|
361
|
+
catch (e) {
|
|
362
|
+
return { error: `image_to_video failed: ${e instanceof Error ? e.message : String(e)}` };
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
userMessage(params) {
|
|
366
|
+
return `Animating image with ${String(params["provider"])}:${String(params["model"])}`;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
function int16ToUint8(samples) {
|
|
370
|
+
const bytes = new Uint8Array(samples.byteLength);
|
|
371
|
+
const view = new DataView(bytes.buffer);
|
|
372
|
+
for (let i = 0; i < samples.length; i++) {
|
|
373
|
+
view.setInt16(i * 2, samples[i], true);
|
|
374
|
+
}
|
|
375
|
+
return bytes;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Wrap raw little-endian int16 PCM bytes in a minimal RIFF/WAVE container so
|
|
379
|
+
* the file is playable. Defaults match OpenAI's TTS PCM stream (24 kHz mono).
|
|
380
|
+
*/
|
|
381
|
+
function wrapPcmAsWav(pcm, sampleRate = 24000, channels = 1) {
|
|
382
|
+
const bitsPerSample = 16;
|
|
383
|
+
const byteRate = (sampleRate * channels * bitsPerSample) / 8;
|
|
384
|
+
const blockAlign = (channels * bitsPerSample) / 8;
|
|
385
|
+
const header = new Uint8Array(44);
|
|
386
|
+
const v = new DataView(header.buffer);
|
|
387
|
+
const w = (s, off) => {
|
|
388
|
+
for (let i = 0; i < s.length; i++)
|
|
389
|
+
v.setUint8(off + i, s.charCodeAt(i));
|
|
390
|
+
};
|
|
391
|
+
w("RIFF", 0);
|
|
392
|
+
v.setUint32(4, 36 + pcm.length, true);
|
|
393
|
+
w("WAVE", 8);
|
|
394
|
+
w("fmt ", 12);
|
|
395
|
+
v.setUint32(16, 16, true); // PCM chunk size
|
|
396
|
+
v.setUint16(20, 1, true); // PCM format
|
|
397
|
+
v.setUint16(22, channels, true);
|
|
398
|
+
v.setUint32(24, sampleRate, true);
|
|
399
|
+
v.setUint32(28, byteRate, true);
|
|
400
|
+
v.setUint16(32, blockAlign, true);
|
|
401
|
+
v.setUint16(34, bitsPerSample, true);
|
|
402
|
+
w("data", 36);
|
|
403
|
+
v.setUint32(40, pcm.length, true);
|
|
404
|
+
const out = new Uint8Array(header.length + pcm.length);
|
|
405
|
+
out.set(header, 0);
|
|
406
|
+
out.set(pcm, header.length);
|
|
407
|
+
return out;
|
|
408
|
+
}
|
|
409
|
+
const AUDIO_EXT_TO_FORMAT = {
|
|
410
|
+
mp3: "mp3",
|
|
411
|
+
wav: "wav",
|
|
412
|
+
flac: "flac",
|
|
413
|
+
opus: "opus",
|
|
414
|
+
ogg: "opus",
|
|
415
|
+
aac: "aac",
|
|
416
|
+
m4a: "aac"
|
|
417
|
+
};
|
|
418
|
+
function audioFormatFromOutputFile(outputFile) {
|
|
419
|
+
if (!outputFile)
|
|
420
|
+
return null;
|
|
421
|
+
const ext = path.extname(outputFile).slice(1).toLowerCase();
|
|
422
|
+
return AUDIO_EXT_TO_FORMAT[ext] ?? null;
|
|
423
|
+
}
|
|
424
|
+
function concatUint8(parts) {
|
|
425
|
+
let total = 0;
|
|
426
|
+
for (const p of parts)
|
|
427
|
+
total += p.length;
|
|
428
|
+
const out = new Uint8Array(total);
|
|
429
|
+
let offset = 0;
|
|
430
|
+
for (const p of parts) {
|
|
431
|
+
out.set(p, offset);
|
|
432
|
+
offset += p.length;
|
|
433
|
+
}
|
|
434
|
+
return out;
|
|
435
|
+
}
|
|
436
|
+
export class GenerateSpeechTool extends Tool {
|
|
437
|
+
name = "generate_speech";
|
|
438
|
+
description = "Synthesize speech audio from text using a provider+model selected via find_model (capability=text_to_speech). Result is saved as an asset (asset:// URI returned).";
|
|
439
|
+
inputSchema = {
|
|
440
|
+
type: "object",
|
|
441
|
+
properties: {
|
|
442
|
+
provider: { type: "string" },
|
|
443
|
+
model: { type: "string" },
|
|
444
|
+
text: { type: "string", description: "Text to speak." },
|
|
445
|
+
output_file: {
|
|
446
|
+
type: "string",
|
|
447
|
+
description: "Optional workspace-relative path to also write the audio file (mp3/wav/pcm depending on provider)."
|
|
448
|
+
},
|
|
449
|
+
voice: { type: "string" },
|
|
450
|
+
speed: { type: "number", description: "Speech speed (e.g. 0.25–4.0)." }
|
|
451
|
+
},
|
|
452
|
+
required: ["provider", "model", "text"]
|
|
453
|
+
};
|
|
454
|
+
async process(context, params) {
|
|
455
|
+
const m = parseModelArgs(params);
|
|
456
|
+
if ("error" in m)
|
|
457
|
+
return m;
|
|
458
|
+
const text = params["text"];
|
|
459
|
+
if (typeof text !== "string" || !text)
|
|
460
|
+
return { error: "text is required" };
|
|
461
|
+
const outputFile = typeof params["output_file"] === "string"
|
|
462
|
+
? params["output_file"]
|
|
463
|
+
: undefined;
|
|
464
|
+
const desiredFormat = audioFormatFromOutputFile(outputFile) ?? "mp3";
|
|
465
|
+
try {
|
|
466
|
+
// Preferred path: ask the provider for fully-encoded audio in the
|
|
467
|
+
// desired container (mp3/wav/flac/...). Returns null when the provider
|
|
468
|
+
// doesn't support encoded TTS — we then fall through to streaming PCM.
|
|
469
|
+
let audio = null;
|
|
470
|
+
let mimeType;
|
|
471
|
+
let outputFileFinal = outputFile;
|
|
472
|
+
try {
|
|
473
|
+
const provider = await context.getProvider(m.provider);
|
|
474
|
+
const encoded = await provider.textToSpeechEncoded({
|
|
475
|
+
text,
|
|
476
|
+
model: m.model,
|
|
477
|
+
voice: params["voice"],
|
|
478
|
+
speed: params["speed"],
|
|
479
|
+
audioFormat: desiredFormat
|
|
480
|
+
});
|
|
481
|
+
if (encoded && encoded.data) {
|
|
482
|
+
audio = encoded.data;
|
|
483
|
+
mimeType = encoded.mimeType;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
catch {
|
|
487
|
+
// Fall through to streaming path.
|
|
488
|
+
}
|
|
489
|
+
if (!audio) {
|
|
490
|
+
// Streaming path — provider returns either pre-encoded chunks
|
|
491
|
+
// (carrying mimeType) or raw int16 PCM samples that we must wrap in
|
|
492
|
+
// a WAV container before writing to disk so the file is playable.
|
|
493
|
+
const parts = [];
|
|
494
|
+
let pcmOnly = true;
|
|
495
|
+
for await (const item of context.streamProviderPrediction({
|
|
496
|
+
provider: m.provider,
|
|
497
|
+
capability: "text_to_speech",
|
|
498
|
+
model: m.model,
|
|
499
|
+
params: {
|
|
500
|
+
text,
|
|
501
|
+
voice: params["voice"],
|
|
502
|
+
speed: params["speed"]
|
|
503
|
+
}
|
|
504
|
+
})) {
|
|
505
|
+
const chunk = item;
|
|
506
|
+
if (chunk.data instanceof Uint8Array) {
|
|
507
|
+
parts.push(chunk.data);
|
|
508
|
+
if (chunk.mimeType)
|
|
509
|
+
mimeType = chunk.mimeType;
|
|
510
|
+
pcmOnly = false;
|
|
511
|
+
}
|
|
512
|
+
else if (typeof chunk.data === "string") {
|
|
513
|
+
parts.push(Buffer.from(chunk.data, "base64"));
|
|
514
|
+
if (chunk.mimeType)
|
|
515
|
+
mimeType = chunk.mimeType;
|
|
516
|
+
pcmOnly = false;
|
|
517
|
+
}
|
|
518
|
+
else if (chunk.samples) {
|
|
519
|
+
parts.push(int16ToUint8(chunk.samples));
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
if (parts.length === 0)
|
|
523
|
+
return { error: "Provider returned no audio data" };
|
|
524
|
+
const merged = concatUint8(parts);
|
|
525
|
+
if (pcmOnly && !mimeType) {
|
|
526
|
+
// Wrap raw PCM in WAV so the bytes are playable. Rename .mp3 →
|
|
527
|
+
// .wav since the actual data is now WAV, not MP3.
|
|
528
|
+
audio = wrapPcmAsWav(merged);
|
|
529
|
+
mimeType = "audio/wav";
|
|
530
|
+
if (outputFileFinal) {
|
|
531
|
+
const dir = path.dirname(outputFileFinal);
|
|
532
|
+
const base = path.basename(outputFileFinal, path.extname(outputFileFinal));
|
|
533
|
+
outputFileFinal = path.join(dir === "." ? "" : dir, `${base}.wav`);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
audio = merged;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
const persisted = await persistOutput(context, audio, {
|
|
541
|
+
namePrefix: "generated-speech",
|
|
542
|
+
mime: mimeType ?? "audio/mpeg",
|
|
543
|
+
outputFile: outputFileFinal
|
|
544
|
+
});
|
|
545
|
+
return {
|
|
546
|
+
type: "audio",
|
|
547
|
+
provider: m.provider,
|
|
548
|
+
model: m.model,
|
|
549
|
+
...persisted
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
catch (e) {
|
|
553
|
+
return { error: `text_to_speech failed: ${e instanceof Error ? e.message : String(e)}` };
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
userMessage(params) {
|
|
557
|
+
return `Synthesizing speech with ${String(params["provider"])}:${String(params["model"])}`;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
export class TranscribeAudioTool extends Tool {
|
|
561
|
+
name = "transcribe_audio";
|
|
562
|
+
description = "Transcribe an audio file to text using a provider+model selected via find_model (capability=automatic_speech_recognition). Source can be a workspace path or asset:// URI.";
|
|
563
|
+
inputSchema = {
|
|
564
|
+
type: "object",
|
|
565
|
+
properties: {
|
|
566
|
+
provider: { type: "string" },
|
|
567
|
+
model: { type: "string" },
|
|
568
|
+
input_file: {
|
|
569
|
+
type: "string",
|
|
570
|
+
description: "Workspace-relative path of the audio file to transcribe (or asset:// URI)."
|
|
571
|
+
},
|
|
572
|
+
language: {
|
|
573
|
+
type: "string",
|
|
574
|
+
description: "Optional ISO 639-1 language hint (e.g. 'en')."
|
|
575
|
+
},
|
|
576
|
+
prompt: {
|
|
577
|
+
type: "string",
|
|
578
|
+
description: "Optional context to bias the model."
|
|
579
|
+
}
|
|
580
|
+
},
|
|
581
|
+
required: ["provider", "model", "input_file"]
|
|
582
|
+
};
|
|
583
|
+
async process(context, params) {
|
|
584
|
+
const m = parseModelArgs(params);
|
|
585
|
+
if ("error" in m)
|
|
586
|
+
return m;
|
|
587
|
+
const inputFile = params["input_file"];
|
|
588
|
+
if (typeof inputFile !== "string" || !inputFile)
|
|
589
|
+
return { error: "input_file is required" };
|
|
590
|
+
try {
|
|
591
|
+
const audio = await readWorkspaceOrAssetFile(context, inputFile);
|
|
592
|
+
const result = (await context.runProviderPrediction({
|
|
593
|
+
provider: m.provider,
|
|
594
|
+
capability: "automatic_speech_recognition",
|
|
595
|
+
model: m.model,
|
|
596
|
+
params: {
|
|
597
|
+
audio,
|
|
598
|
+
language: params["language"],
|
|
599
|
+
prompt: params["prompt"]
|
|
600
|
+
}
|
|
601
|
+
}));
|
|
602
|
+
const text = String(result.text ?? "");
|
|
603
|
+
return {
|
|
604
|
+
type: "transcription",
|
|
605
|
+
provider: m.provider,
|
|
606
|
+
model: m.model,
|
|
607
|
+
text: text.length > MAX_INLINE_TEXT_PREVIEW
|
|
608
|
+
? text.slice(0, MAX_INLINE_TEXT_PREVIEW) +
|
|
609
|
+
`… [${text.length - MAX_INLINE_TEXT_PREVIEW} chars truncated]`
|
|
610
|
+
: text,
|
|
611
|
+
full_length: text.length,
|
|
612
|
+
chunks: Array.isArray(result.chunks) ? result.chunks.length : 0
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
catch (e) {
|
|
616
|
+
return { error: `transcribe failed: ${e instanceof Error ? e.message : String(e)}` };
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
userMessage(params) {
|
|
620
|
+
return `Transcribing audio with ${String(params["provider"])}:${String(params["model"])}`;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
/* ------------------------------------------------------------------ */
|
|
624
|
+
/* Embeddings */
|
|
625
|
+
/* ------------------------------------------------------------------ */
|
|
626
|
+
export class EmbedTextTool extends Tool {
|
|
627
|
+
name = "embed_text";
|
|
628
|
+
description = "Compute embedding vector(s) for a text or list of texts using a provider+model selected via find_model (capability=generate_embedding).";
|
|
629
|
+
inputSchema = {
|
|
630
|
+
type: "object",
|
|
631
|
+
properties: {
|
|
632
|
+
provider: { type: "string" },
|
|
633
|
+
model: { type: "string" },
|
|
634
|
+
text: {
|
|
635
|
+
oneOf: [
|
|
636
|
+
{ type: "string" },
|
|
637
|
+
{ type: "array", items: { type: "string" } }
|
|
638
|
+
],
|
|
639
|
+
description: "A single string or an array of strings to embed."
|
|
640
|
+
},
|
|
641
|
+
dimensions: {
|
|
642
|
+
type: "number",
|
|
643
|
+
description: "Optional target dimensions if the model supports truncation."
|
|
644
|
+
}
|
|
645
|
+
},
|
|
646
|
+
required: ["provider", "model", "text"]
|
|
647
|
+
};
|
|
648
|
+
async process(context, params) {
|
|
649
|
+
const m = parseModelArgs(params);
|
|
650
|
+
if ("error" in m)
|
|
651
|
+
return m;
|
|
652
|
+
const text = params["text"];
|
|
653
|
+
if (typeof text !== "string" && !Array.isArray(text))
|
|
654
|
+
return { error: "text must be a string or array of strings" };
|
|
655
|
+
try {
|
|
656
|
+
const result = (await context.runProviderPrediction({
|
|
657
|
+
provider: m.provider,
|
|
658
|
+
capability: "generate_embedding",
|
|
659
|
+
model: m.model,
|
|
660
|
+
params: {
|
|
661
|
+
text,
|
|
662
|
+
dimensions: params["dimensions"]
|
|
663
|
+
}
|
|
664
|
+
}));
|
|
665
|
+
return {
|
|
666
|
+
type: "embedding",
|
|
667
|
+
provider: m.provider,
|
|
668
|
+
model: m.model,
|
|
669
|
+
count: result.length,
|
|
670
|
+
dimensions: result[0]?.length ?? 0,
|
|
671
|
+
embeddings: result
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
catch (e) {
|
|
675
|
+
return { error: `generate_embedding failed: ${e instanceof Error ? e.message : String(e)}` };
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
userMessage(params) {
|
|
679
|
+
return `Embedding text with ${String(params["provider"])}:${String(params["model"])}`;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
//# sourceMappingURL=media-tools.js.map
|