@radaros/core 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +265 -1
- package/dist/index.js +723 -33
- package/package.json +6 -2
- package/src/a2a/a2a-remote-agent.ts +270 -0
- package/src/a2a/types.ts +142 -0
- package/src/index.ts +27 -1
- package/src/mcp/mcp-client.ts +264 -0
- package/src/models/providers/vertex.ts +400 -0
- package/src/models/registry.ts +17 -0
- package/src/tools/tool-executor.ts +17 -9
- package/src/tools/types.ts +2 -0
package/dist/index.js
CHANGED
|
@@ -199,14 +199,22 @@ var ToolExecutor = class {
|
|
|
199
199
|
const { zodToJsonSchema } = _require("zod-to-json-schema");
|
|
200
200
|
const defs = [];
|
|
201
201
|
for (const tool of this.tools.values()) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
202
|
+
if (tool.rawJsonSchema) {
|
|
203
|
+
defs.push({
|
|
204
|
+
name: tool.name,
|
|
205
|
+
description: tool.description,
|
|
206
|
+
parameters: tool.rawJsonSchema
|
|
207
|
+
});
|
|
208
|
+
} else {
|
|
209
|
+
const jsonSchema = zodToJsonSchema(tool.parameters, {
|
|
210
|
+
target: "openApi3"
|
|
211
|
+
});
|
|
212
|
+
defs.push({
|
|
213
|
+
name: tool.name,
|
|
214
|
+
description: tool.description,
|
|
215
|
+
parameters: jsonSchema
|
|
216
|
+
});
|
|
217
|
+
}
|
|
210
218
|
}
|
|
211
219
|
return defs;
|
|
212
220
|
}
|
|
@@ -2359,6 +2367,291 @@ var OllamaProvider = class {
|
|
|
2359
2367
|
}
|
|
2360
2368
|
};
|
|
2361
2369
|
|
|
2370
|
+
// src/models/providers/vertex.ts
|
|
2371
|
+
import { createRequire as createRequire7 } from "module";
|
|
2372
|
+
var _require7 = createRequire7(import.meta.url);
|
|
2373
|
+
var VertexAIProvider = class {
|
|
2374
|
+
providerId = "vertex";
|
|
2375
|
+
modelId;
|
|
2376
|
+
ai = null;
|
|
2377
|
+
GoogleGenAICtor;
|
|
2378
|
+
project;
|
|
2379
|
+
location;
|
|
2380
|
+
constructor(modelId, config) {
|
|
2381
|
+
this.modelId = modelId;
|
|
2382
|
+
this.project = config?.project ?? process.env.GOOGLE_CLOUD_PROJECT ?? process.env.GCLOUD_PROJECT ?? "";
|
|
2383
|
+
this.location = config?.location ?? process.env.GOOGLE_CLOUD_LOCATION ?? process.env.GOOGLE_CLOUD_REGION ?? "us-central1";
|
|
2384
|
+
if (!this.project) {
|
|
2385
|
+
throw new Error(
|
|
2386
|
+
"VertexAIProvider: 'project' is required. Pass it in config or set GOOGLE_CLOUD_PROJECT env var."
|
|
2387
|
+
);
|
|
2388
|
+
}
|
|
2389
|
+
try {
|
|
2390
|
+
const { GoogleGenAI } = _require7("@google/genai");
|
|
2391
|
+
this.GoogleGenAICtor = GoogleGenAI;
|
|
2392
|
+
} catch {
|
|
2393
|
+
throw new Error(
|
|
2394
|
+
"@google/genai is required for VertexAIProvider. Install it: npm install @google/genai"
|
|
2395
|
+
);
|
|
2396
|
+
}
|
|
2397
|
+
}
|
|
2398
|
+
getClient() {
|
|
2399
|
+
if (this.ai) return this.ai;
|
|
2400
|
+
this.ai = new this.GoogleGenAICtor({
|
|
2401
|
+
vertexai: true,
|
|
2402
|
+
project: this.project,
|
|
2403
|
+
location: this.location
|
|
2404
|
+
});
|
|
2405
|
+
return this.ai;
|
|
2406
|
+
}
|
|
2407
|
+
async generate(messages, options) {
|
|
2408
|
+
const { systemInstruction, contents } = this.toGoogleMessages(messages);
|
|
2409
|
+
const config = {};
|
|
2410
|
+
if (options?.temperature !== void 0)
|
|
2411
|
+
config.temperature = options.temperature;
|
|
2412
|
+
if (options?.maxTokens !== void 0)
|
|
2413
|
+
config.maxOutputTokens = options.maxTokens;
|
|
2414
|
+
if (options?.topP !== void 0) config.topP = options.topP;
|
|
2415
|
+
if (options?.stop) config.stopSequences = options.stop;
|
|
2416
|
+
if (options?.responseFormat) {
|
|
2417
|
+
config.responseMimeType = "application/json";
|
|
2418
|
+
const rf = options.responseFormat;
|
|
2419
|
+
if (typeof rf === "object" && rf !== null && "type" in rf && rf.type === "json_schema" && "schema" in rf && rf.schema) {
|
|
2420
|
+
config.responseSchema = this.cleanJsonSchema(
|
|
2421
|
+
rf.schema
|
|
2422
|
+
);
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
const params = {
|
|
2426
|
+
model: this.modelId,
|
|
2427
|
+
contents,
|
|
2428
|
+
config
|
|
2429
|
+
};
|
|
2430
|
+
if (systemInstruction) params.systemInstruction = systemInstruction;
|
|
2431
|
+
if (options?.tools?.length) {
|
|
2432
|
+
params.tools = [
|
|
2433
|
+
{ functionDeclarations: this.toGoogleTools(options.tools) }
|
|
2434
|
+
];
|
|
2435
|
+
}
|
|
2436
|
+
const client = this.getClient();
|
|
2437
|
+
const response = await client.models.generateContent(params);
|
|
2438
|
+
return this.normalizeResponse(response);
|
|
2439
|
+
}
|
|
2440
|
+
async *stream(messages, options) {
|
|
2441
|
+
const { systemInstruction, contents } = this.toGoogleMessages(messages);
|
|
2442
|
+
const config = {};
|
|
2443
|
+
if (options?.temperature !== void 0)
|
|
2444
|
+
config.temperature = options.temperature;
|
|
2445
|
+
if (options?.maxTokens !== void 0)
|
|
2446
|
+
config.maxOutputTokens = options.maxTokens;
|
|
2447
|
+
if (options?.topP !== void 0) config.topP = options.topP;
|
|
2448
|
+
if (options?.stop) config.stopSequences = options.stop;
|
|
2449
|
+
const params = {
|
|
2450
|
+
model: this.modelId,
|
|
2451
|
+
contents,
|
|
2452
|
+
config
|
|
2453
|
+
};
|
|
2454
|
+
if (systemInstruction) params.systemInstruction = systemInstruction;
|
|
2455
|
+
if (options?.tools?.length) {
|
|
2456
|
+
params.tools = [
|
|
2457
|
+
{ functionDeclarations: this.toGoogleTools(options.tools) }
|
|
2458
|
+
];
|
|
2459
|
+
}
|
|
2460
|
+
const client = this.getClient();
|
|
2461
|
+
const streamResult = await client.models.generateContentStream(params);
|
|
2462
|
+
let toolCallCounter = 0;
|
|
2463
|
+
for await (const chunk of streamResult) {
|
|
2464
|
+
const candidate = chunk.candidates?.[0];
|
|
2465
|
+
if (!candidate?.content?.parts) continue;
|
|
2466
|
+
for (const part of candidate.content.parts) {
|
|
2467
|
+
if (part.text) {
|
|
2468
|
+
yield { type: "text", text: part.text };
|
|
2469
|
+
}
|
|
2470
|
+
if (part.functionCall) {
|
|
2471
|
+
const id = `vertex_tc_${toolCallCounter++}`;
|
|
2472
|
+
yield {
|
|
2473
|
+
type: "tool_call_start",
|
|
2474
|
+
toolCall: { id, name: part.functionCall.name }
|
|
2475
|
+
};
|
|
2476
|
+
yield {
|
|
2477
|
+
type: "tool_call_delta",
|
|
2478
|
+
toolCallId: id,
|
|
2479
|
+
argumentsDelta: JSON.stringify(part.functionCall.args ?? {})
|
|
2480
|
+
};
|
|
2481
|
+
yield { type: "tool_call_end", toolCallId: id };
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2484
|
+
if (candidate.finishReason) {
|
|
2485
|
+
let finishReason = "stop";
|
|
2486
|
+
if (candidate.finishReason === "STOP" || candidate.finishReason === "END_TURN")
|
|
2487
|
+
finishReason = "stop";
|
|
2488
|
+
else if (candidate.finishReason === "MAX_TOKENS")
|
|
2489
|
+
finishReason = "length";
|
|
2490
|
+
else if (candidate.finishReason === "SAFETY")
|
|
2491
|
+
finishReason = "content_filter";
|
|
2492
|
+
const hasToolCalls = candidate.content?.parts?.some(
|
|
2493
|
+
(p) => p.functionCall
|
|
2494
|
+
);
|
|
2495
|
+
if (hasToolCalls) finishReason = "tool_calls";
|
|
2496
|
+
yield {
|
|
2497
|
+
type: "finish",
|
|
2498
|
+
finishReason,
|
|
2499
|
+
usage: chunk.usageMetadata ? {
|
|
2500
|
+
promptTokens: chunk.usageMetadata.promptTokenCount ?? 0,
|
|
2501
|
+
completionTokens: chunk.usageMetadata.candidatesTokenCount ?? 0,
|
|
2502
|
+
totalTokens: chunk.usageMetadata.totalTokenCount ?? 0
|
|
2503
|
+
} : void 0
|
|
2504
|
+
};
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
// ── Message conversion (identical to GoogleProvider) ─────────────────────
|
|
2509
|
+
toGoogleMessages(messages) {
|
|
2510
|
+
let systemInstruction;
|
|
2511
|
+
const contents = [];
|
|
2512
|
+
for (const msg of messages) {
|
|
2513
|
+
if (msg.role === "system") {
|
|
2514
|
+
systemInstruction = getTextContent(msg.content) || void 0;
|
|
2515
|
+
continue;
|
|
2516
|
+
}
|
|
2517
|
+
if (msg.role === "user") {
|
|
2518
|
+
if (isMultiModal(msg.content)) {
|
|
2519
|
+
contents.push({
|
|
2520
|
+
role: "user",
|
|
2521
|
+
parts: msg.content.map((p) => this.partToGoogle(p))
|
|
2522
|
+
});
|
|
2523
|
+
} else {
|
|
2524
|
+
contents.push({
|
|
2525
|
+
role: "user",
|
|
2526
|
+
parts: [{ text: msg.content ?? "" }]
|
|
2527
|
+
});
|
|
2528
|
+
}
|
|
2529
|
+
continue;
|
|
2530
|
+
}
|
|
2531
|
+
if (msg.role === "assistant") {
|
|
2532
|
+
const parts = [];
|
|
2533
|
+
if (msg.content) parts.push({ text: msg.content });
|
|
2534
|
+
if (msg.toolCalls) {
|
|
2535
|
+
for (const tc of msg.toolCalls) {
|
|
2536
|
+
parts.push({
|
|
2537
|
+
functionCall: { name: tc.name, args: tc.arguments }
|
|
2538
|
+
});
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
if (parts.length === 0) parts.push({ text: "" });
|
|
2542
|
+
contents.push({ role: "model", parts });
|
|
2543
|
+
continue;
|
|
2544
|
+
}
|
|
2545
|
+
if (msg.role === "tool") {
|
|
2546
|
+
contents.push({
|
|
2547
|
+
role: "function",
|
|
2548
|
+
parts: [
|
|
2549
|
+
{
|
|
2550
|
+
functionResponse: {
|
|
2551
|
+
name: msg.name ?? "unknown",
|
|
2552
|
+
response: { result: msg.content ?? "" }
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
]
|
|
2556
|
+
});
|
|
2557
|
+
continue;
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
2560
|
+
return { systemInstruction, contents };
|
|
2561
|
+
}
|
|
2562
|
+
partToGoogle(part) {
|
|
2563
|
+
switch (part.type) {
|
|
2564
|
+
case "text":
|
|
2565
|
+
return { text: part.text };
|
|
2566
|
+
case "image":
|
|
2567
|
+
case "audio":
|
|
2568
|
+
case "file": {
|
|
2569
|
+
const isUrl = part.data.startsWith("http://") || part.data.startsWith("https://");
|
|
2570
|
+
if (isUrl) {
|
|
2571
|
+
return {
|
|
2572
|
+
fileData: {
|
|
2573
|
+
fileUri: part.data,
|
|
2574
|
+
mimeType: part.mimeType ?? (part.type === "image" ? "image/png" : "application/octet-stream")
|
|
2575
|
+
}
|
|
2576
|
+
};
|
|
2577
|
+
}
|
|
2578
|
+
return {
|
|
2579
|
+
inlineData: {
|
|
2580
|
+
data: part.data,
|
|
2581
|
+
mimeType: part.mimeType ?? (part.type === "image" ? "image/png" : part.type === "audio" ? "audio/mp3" : "application/octet-stream")
|
|
2582
|
+
}
|
|
2583
|
+
};
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
toGoogleTools(tools) {
|
|
2588
|
+
return tools.map((t) => ({
|
|
2589
|
+
name: t.name,
|
|
2590
|
+
description: t.description,
|
|
2591
|
+
parameters: t.parameters
|
|
2592
|
+
}));
|
|
2593
|
+
}
|
|
2594
|
+
cleanJsonSchema(schema) {
|
|
2595
|
+
const cleaned = { ...schema };
|
|
2596
|
+
delete cleaned["$schema"];
|
|
2597
|
+
delete cleaned["$ref"];
|
|
2598
|
+
delete cleaned["additionalProperties"];
|
|
2599
|
+
if (cleaned.properties && typeof cleaned.properties === "object") {
|
|
2600
|
+
const props = {};
|
|
2601
|
+
for (const [key, val] of Object.entries(
|
|
2602
|
+
cleaned.properties
|
|
2603
|
+
)) {
|
|
2604
|
+
props[key] = typeof val === "object" && val ? this.cleanJsonSchema(val) : val;
|
|
2605
|
+
}
|
|
2606
|
+
cleaned.properties = props;
|
|
2607
|
+
}
|
|
2608
|
+
if (cleaned.items && typeof cleaned.items === "object") {
|
|
2609
|
+
cleaned.items = this.cleanJsonSchema(
|
|
2610
|
+
cleaned.items
|
|
2611
|
+
);
|
|
2612
|
+
}
|
|
2613
|
+
return cleaned;
|
|
2614
|
+
}
|
|
2615
|
+
normalizeResponse(response) {
|
|
2616
|
+
const candidate = response.candidates?.[0];
|
|
2617
|
+
const parts = candidate?.content?.parts ?? [];
|
|
2618
|
+
let textContent = "";
|
|
2619
|
+
const toolCalls = [];
|
|
2620
|
+
let toolCallCounter = 0;
|
|
2621
|
+
for (const part of parts) {
|
|
2622
|
+
if (part.text) textContent += part.text;
|
|
2623
|
+
if (part.functionCall) {
|
|
2624
|
+
toolCalls.push({
|
|
2625
|
+
id: `vertex_tc_${toolCallCounter++}`,
|
|
2626
|
+
name: part.functionCall.name,
|
|
2627
|
+
arguments: part.functionCall.args ?? {}
|
|
2628
|
+
});
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2631
|
+
const usage = {
|
|
2632
|
+
promptTokens: response.usageMetadata?.promptTokenCount ?? 0,
|
|
2633
|
+
completionTokens: response.usageMetadata?.candidatesTokenCount ?? 0,
|
|
2634
|
+
totalTokens: response.usageMetadata?.totalTokenCount ?? 0
|
|
2635
|
+
};
|
|
2636
|
+
let finishReason = "stop";
|
|
2637
|
+
if (toolCalls.length > 0) finishReason = "tool_calls";
|
|
2638
|
+
else if (candidate?.finishReason === "MAX_TOKENS")
|
|
2639
|
+
finishReason = "length";
|
|
2640
|
+
else if (candidate?.finishReason === "SAFETY")
|
|
2641
|
+
finishReason = "content_filter";
|
|
2642
|
+
return {
|
|
2643
|
+
message: {
|
|
2644
|
+
role: "assistant",
|
|
2645
|
+
content: textContent || null,
|
|
2646
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : void 0
|
|
2647
|
+
},
|
|
2648
|
+
usage,
|
|
2649
|
+
finishReason,
|
|
2650
|
+
raw: response
|
|
2651
|
+
};
|
|
2652
|
+
}
|
|
2653
|
+
};
|
|
2654
|
+
|
|
2362
2655
|
// src/models/registry.ts
|
|
2363
2656
|
var ModelRegistry = class {
|
|
2364
2657
|
factories = /* @__PURE__ */ new Map();
|
|
@@ -2407,6 +2700,16 @@ function google(modelId, config) {
|
|
|
2407
2700
|
function ollama(modelId, config) {
|
|
2408
2701
|
return registry.resolve("ollama", modelId, config);
|
|
2409
2702
|
}
|
|
2703
|
+
registry.register(
|
|
2704
|
+
"vertex",
|
|
2705
|
+
(modelId, config) => new VertexAIProvider(
|
|
2706
|
+
modelId,
|
|
2707
|
+
config
|
|
2708
|
+
)
|
|
2709
|
+
);
|
|
2710
|
+
function vertex(modelId, config) {
|
|
2711
|
+
return registry.resolve("vertex", modelId, config);
|
|
2712
|
+
}
|
|
2410
2713
|
|
|
2411
2714
|
// src/tools/define-tool.ts
|
|
2412
2715
|
function defineTool(config) {
|
|
@@ -2419,13 +2722,13 @@ function defineTool(config) {
|
|
|
2419
2722
|
}
|
|
2420
2723
|
|
|
2421
2724
|
// src/storage/sqlite.ts
|
|
2422
|
-
import { createRequire as
|
|
2423
|
-
var
|
|
2725
|
+
import { createRequire as createRequire8 } from "module";
|
|
2726
|
+
var _require8 = createRequire8(import.meta.url);
|
|
2424
2727
|
var SqliteStorage = class {
|
|
2425
2728
|
db;
|
|
2426
2729
|
constructor(dbPath) {
|
|
2427
2730
|
try {
|
|
2428
|
-
const Database =
|
|
2731
|
+
const Database = _require8("better-sqlite3");
|
|
2429
2732
|
this.db = new Database(dbPath);
|
|
2430
2733
|
this.db.pragma("journal_mode = WAL");
|
|
2431
2734
|
this.db.exec(`
|
|
@@ -2474,13 +2777,13 @@ var SqliteStorage = class {
|
|
|
2474
2777
|
};
|
|
2475
2778
|
|
|
2476
2779
|
// src/storage/postgres.ts
|
|
2477
|
-
import { createRequire as
|
|
2478
|
-
var
|
|
2780
|
+
import { createRequire as createRequire9 } from "module";
|
|
2781
|
+
var _require9 = createRequire9(import.meta.url);
|
|
2479
2782
|
var PostgresStorage = class {
|
|
2480
2783
|
pool;
|
|
2481
2784
|
constructor(connectionString) {
|
|
2482
2785
|
try {
|
|
2483
|
-
const { Pool } =
|
|
2786
|
+
const { Pool } = _require9("pg");
|
|
2484
2787
|
this.pool = new Pool({ connectionString });
|
|
2485
2788
|
} catch {
|
|
2486
2789
|
throw new Error(
|
|
@@ -2541,15 +2844,15 @@ var PostgresStorage = class {
|
|
|
2541
2844
|
};
|
|
2542
2845
|
|
|
2543
2846
|
// src/storage/mongodb.ts
|
|
2544
|
-
import { createRequire as
|
|
2545
|
-
var
|
|
2847
|
+
import { createRequire as createRequire10 } from "module";
|
|
2848
|
+
var _require10 = createRequire10(import.meta.url);
|
|
2546
2849
|
var MongoDBStorage = class {
|
|
2547
2850
|
constructor(uri, dbName = "radaros", collectionName = "kv_store") {
|
|
2548
2851
|
this.uri = uri;
|
|
2549
2852
|
this.dbName = dbName;
|
|
2550
2853
|
this.collectionName = collectionName;
|
|
2551
2854
|
try {
|
|
2552
|
-
const { MongoClient } =
|
|
2855
|
+
const { MongoClient } = _require10("mongodb");
|
|
2553
2856
|
this.client = new MongoClient(uri);
|
|
2554
2857
|
} catch {
|
|
2555
2858
|
throw new Error(
|
|
@@ -2709,8 +3012,8 @@ var InMemoryVectorStore = class extends BaseVectorStore {
|
|
|
2709
3012
|
};
|
|
2710
3013
|
|
|
2711
3014
|
// src/vector/pgvector.ts
|
|
2712
|
-
import { createRequire as
|
|
2713
|
-
var
|
|
3015
|
+
import { createRequire as createRequire11 } from "module";
|
|
3016
|
+
var _require11 = createRequire11(import.meta.url);
|
|
2714
3017
|
var PgVectorStore = class extends BaseVectorStore {
|
|
2715
3018
|
pool;
|
|
2716
3019
|
dimensions;
|
|
@@ -2719,7 +3022,7 @@ var PgVectorStore = class extends BaseVectorStore {
|
|
|
2719
3022
|
super(embedder);
|
|
2720
3023
|
this.dimensions = config.dimensions ?? embedder?.dimensions ?? 1536;
|
|
2721
3024
|
try {
|
|
2722
|
-
const { Pool } =
|
|
3025
|
+
const { Pool } = _require11("pg");
|
|
2723
3026
|
this.pool = new Pool({ connectionString: config.connectionString });
|
|
2724
3027
|
} catch {
|
|
2725
3028
|
throw new Error(
|
|
@@ -2838,9 +3141,9 @@ var PgVectorStore = class extends BaseVectorStore {
|
|
|
2838
3141
|
};
|
|
2839
3142
|
|
|
2840
3143
|
// src/vector/qdrant.ts
|
|
2841
|
-
import { createRequire as
|
|
3144
|
+
import { createRequire as createRequire12 } from "module";
|
|
2842
3145
|
import { createHash } from "crypto";
|
|
2843
|
-
var
|
|
3146
|
+
var _require12 = createRequire12(import.meta.url);
|
|
2844
3147
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2845
3148
|
function stringToUUID(str) {
|
|
2846
3149
|
const hex = createHash("md5").update(str).digest("hex");
|
|
@@ -2865,7 +3168,7 @@ var QdrantVectorStore = class extends BaseVectorStore {
|
|
|
2865
3168
|
super(embedder);
|
|
2866
3169
|
this.dimensions = config.dimensions ?? embedder?.dimensions ?? 1536;
|
|
2867
3170
|
try {
|
|
2868
|
-
const { QdrantClient } =
|
|
3171
|
+
const { QdrantClient } = _require12("@qdrant/js-client-rest");
|
|
2869
3172
|
this.client = new QdrantClient({
|
|
2870
3173
|
url: config.url ?? "http://localhost:6333",
|
|
2871
3174
|
apiKey: config.apiKey,
|
|
@@ -2998,8 +3301,8 @@ var QdrantVectorStore = class extends BaseVectorStore {
|
|
|
2998
3301
|
};
|
|
2999
3302
|
|
|
3000
3303
|
// src/vector/mongodb.ts
|
|
3001
|
-
import { createRequire as
|
|
3002
|
-
var
|
|
3304
|
+
import { createRequire as createRequire13 } from "module";
|
|
3305
|
+
var _require13 = createRequire13(import.meta.url);
|
|
3003
3306
|
var MongoDBVectorStore = class extends BaseVectorStore {
|
|
3004
3307
|
client;
|
|
3005
3308
|
db;
|
|
@@ -3011,7 +3314,7 @@ var MongoDBVectorStore = class extends BaseVectorStore {
|
|
|
3011
3314
|
this.indexName = config.indexName ?? "vector_index";
|
|
3012
3315
|
this.dbName = config.dbName ?? "radaros_vectors";
|
|
3013
3316
|
try {
|
|
3014
|
-
const { MongoClient } =
|
|
3317
|
+
const { MongoClient } = _require13("mongodb");
|
|
3015
3318
|
this.client = new MongoClient(config.uri);
|
|
3016
3319
|
} catch {
|
|
3017
3320
|
throw new Error(
|
|
@@ -3182,8 +3485,8 @@ function cosine(a, b) {
|
|
|
3182
3485
|
}
|
|
3183
3486
|
|
|
3184
3487
|
// src/vector/embeddings/openai.ts
|
|
3185
|
-
import { createRequire as
|
|
3186
|
-
var
|
|
3488
|
+
import { createRequire as createRequire14 } from "module";
|
|
3489
|
+
var _require14 = createRequire14(import.meta.url);
|
|
3187
3490
|
var MODEL_DIMENSIONS = {
|
|
3188
3491
|
"text-embedding-3-small": 1536,
|
|
3189
3492
|
"text-embedding-3-large": 3072,
|
|
@@ -3197,7 +3500,7 @@ var OpenAIEmbedding = class {
|
|
|
3197
3500
|
this.model = config.model ?? "text-embedding-3-small";
|
|
3198
3501
|
this.dimensions = config.dimensions ?? MODEL_DIMENSIONS[this.model] ?? 1536;
|
|
3199
3502
|
try {
|
|
3200
|
-
const mod =
|
|
3503
|
+
const mod = _require14("openai");
|
|
3201
3504
|
const OpenAI = mod.default ?? mod;
|
|
3202
3505
|
this.client = new OpenAI({
|
|
3203
3506
|
apiKey: config.apiKey ?? process.env.OPENAI_API_KEY,
|
|
@@ -3228,8 +3531,8 @@ var OpenAIEmbedding = class {
|
|
|
3228
3531
|
};
|
|
3229
3532
|
|
|
3230
3533
|
// src/vector/embeddings/google.ts
|
|
3231
|
-
import { createRequire as
|
|
3232
|
-
var
|
|
3534
|
+
import { createRequire as createRequire15 } from "module";
|
|
3535
|
+
var _require15 = createRequire15(import.meta.url);
|
|
3233
3536
|
var MODEL_DIMENSIONS2 = {
|
|
3234
3537
|
"text-embedding-004": 768,
|
|
3235
3538
|
"embedding-001": 768
|
|
@@ -3242,7 +3545,7 @@ var GoogleEmbedding = class {
|
|
|
3242
3545
|
this.model = config.model ?? "text-embedding-004";
|
|
3243
3546
|
this.dimensions = config.dimensions ?? MODEL_DIMENSIONS2[this.model] ?? 768;
|
|
3244
3547
|
try {
|
|
3245
|
-
const { GoogleGenAI } =
|
|
3548
|
+
const { GoogleGenAI } = _require15("@google/genai");
|
|
3246
3549
|
this.ai = new GoogleGenAI({
|
|
3247
3550
|
apiKey: config.apiKey ?? process.env.GOOGLE_API_KEY
|
|
3248
3551
|
});
|
|
@@ -3422,7 +3725,391 @@ ${summaries.join("\n")}`;
|
|
|
3422
3725
|
await this.storage.delete(SHORT_TERM_NS, sessionId);
|
|
3423
3726
|
}
|
|
3424
3727
|
};
|
|
3728
|
+
|
|
3729
|
+
// src/mcp/mcp-client.ts
|
|
3730
|
+
var MCPToolProvider = class {
|
|
3731
|
+
name;
|
|
3732
|
+
config;
|
|
3733
|
+
client = null;
|
|
3734
|
+
transportInstance = null;
|
|
3735
|
+
tools = [];
|
|
3736
|
+
connected = false;
|
|
3737
|
+
constructor(config) {
|
|
3738
|
+
this.name = config.name;
|
|
3739
|
+
this.config = config;
|
|
3740
|
+
}
|
|
3741
|
+
async connect() {
|
|
3742
|
+
if (this.connected) return;
|
|
3743
|
+
let ClientClass;
|
|
3744
|
+
try {
|
|
3745
|
+
const mod = await import("@modelcontextprotocol/sdk/client/index.js");
|
|
3746
|
+
ClientClass = mod.Client;
|
|
3747
|
+
} catch {
|
|
3748
|
+
throw new Error(
|
|
3749
|
+
"@modelcontextprotocol/sdk is required for MCPToolProvider. Install it: npm install @modelcontextprotocol/sdk"
|
|
3750
|
+
);
|
|
3751
|
+
}
|
|
3752
|
+
this.client = new ClientClass(
|
|
3753
|
+
{ name: `radaros-${this.name}`, version: "1.0.0" },
|
|
3754
|
+
{ capabilities: {} }
|
|
3755
|
+
);
|
|
3756
|
+
if (this.config.transport === "stdio") {
|
|
3757
|
+
if (!this.config.command) {
|
|
3758
|
+
throw new Error("MCPToolProvider: 'command' is required for stdio transport");
|
|
3759
|
+
}
|
|
3760
|
+
const { StdioClientTransport } = await import("@modelcontextprotocol/sdk/client/stdio.js");
|
|
3761
|
+
this.transportInstance = new StdioClientTransport({
|
|
3762
|
+
command: this.config.command,
|
|
3763
|
+
args: this.config.args ?? [],
|
|
3764
|
+
env: { ...process.env, ...this.config.env ?? {} }
|
|
3765
|
+
});
|
|
3766
|
+
} else if (this.config.transport === "http") {
|
|
3767
|
+
if (!this.config.url) {
|
|
3768
|
+
throw new Error("MCPToolProvider: 'url' is required for http transport");
|
|
3769
|
+
}
|
|
3770
|
+
let TransportClass;
|
|
3771
|
+
try {
|
|
3772
|
+
const mod = await import("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
3773
|
+
TransportClass = mod.StreamableHTTPClientTransport;
|
|
3774
|
+
} catch {
|
|
3775
|
+
const mod = await import("@modelcontextprotocol/sdk/client/sse.js");
|
|
3776
|
+
TransportClass = mod.SSEClientTransport;
|
|
3777
|
+
}
|
|
3778
|
+
this.transportInstance = new TransportClass(
|
|
3779
|
+
new URL(this.config.url),
|
|
3780
|
+
{ requestInit: { headers: this.config.headers ?? {} } }
|
|
3781
|
+
);
|
|
3782
|
+
} else {
|
|
3783
|
+
throw new Error(`MCPToolProvider: unsupported transport '${this.config.transport}'`);
|
|
3784
|
+
}
|
|
3785
|
+
await this.client.connect(this.transportInstance);
|
|
3786
|
+
this.connected = true;
|
|
3787
|
+
await this.discoverTools();
|
|
3788
|
+
}
|
|
3789
|
+
async discoverTools() {
|
|
3790
|
+
const { z: z3 } = await import("zod");
|
|
3791
|
+
const result = await this.client.listTools();
|
|
3792
|
+
const mcpTools = result.tools ?? [];
|
|
3793
|
+
this.tools = mcpTools.map((mcpTool) => {
|
|
3794
|
+
const toolName = mcpTool.name;
|
|
3795
|
+
const description = mcpTool.description ?? "";
|
|
3796
|
+
const inputSchema = mcpTool.inputSchema ?? { type: "object", properties: {} };
|
|
3797
|
+
const parameters = this.jsonSchemaToZod(inputSchema, z3);
|
|
3798
|
+
const execute = async (args, _ctx) => {
|
|
3799
|
+
const callResult = await this.client.callTool({
|
|
3800
|
+
name: toolName,
|
|
3801
|
+
arguments: args
|
|
3802
|
+
});
|
|
3803
|
+
const contents = callResult.content ?? [];
|
|
3804
|
+
const textParts = contents.filter((c) => c.type === "text").map((c) => c.text);
|
|
3805
|
+
const text = textParts.join("\n") || JSON.stringify(callResult);
|
|
3806
|
+
const artifacts = contents.filter((c) => c.type !== "text").map((c) => ({
|
|
3807
|
+
type: c.type,
|
|
3808
|
+
data: c.data ?? c.blob ?? c.text,
|
|
3809
|
+
mimeType: c.mimeType
|
|
3810
|
+
}));
|
|
3811
|
+
if (artifacts.length > 0) {
|
|
3812
|
+
return { content: text, artifacts };
|
|
3813
|
+
}
|
|
3814
|
+
return text;
|
|
3815
|
+
};
|
|
3816
|
+
return {
|
|
3817
|
+
name: `${this.name}__${toolName}`,
|
|
3818
|
+
description: `[${this.name}] ${description}`,
|
|
3819
|
+
parameters,
|
|
3820
|
+
execute,
|
|
3821
|
+
rawJsonSchema: inputSchema
|
|
3822
|
+
};
|
|
3823
|
+
});
|
|
3824
|
+
}
|
|
3825
|
+
jsonSchemaToZod(schema, z3) {
|
|
3826
|
+
if (!schema || !schema.properties) {
|
|
3827
|
+
return z3.object({}).passthrough();
|
|
3828
|
+
}
|
|
3829
|
+
const shape = {};
|
|
3830
|
+
const required = schema.required ?? [];
|
|
3831
|
+
for (const [key, prop] of Object.entries(schema.properties)) {
|
|
3832
|
+
let field;
|
|
3833
|
+
switch (prop.type) {
|
|
3834
|
+
case "string":
|
|
3835
|
+
field = z3.string();
|
|
3836
|
+
if (prop.enum) field = z3.enum(prop.enum);
|
|
3837
|
+
break;
|
|
3838
|
+
case "number":
|
|
3839
|
+
case "integer":
|
|
3840
|
+
field = z3.number();
|
|
3841
|
+
break;
|
|
3842
|
+
case "boolean":
|
|
3843
|
+
field = z3.boolean();
|
|
3844
|
+
break;
|
|
3845
|
+
case "array":
|
|
3846
|
+
field = z3.array(z3.any());
|
|
3847
|
+
break;
|
|
3848
|
+
case "object":
|
|
3849
|
+
field = z3.record(z3.any());
|
|
3850
|
+
break;
|
|
3851
|
+
default:
|
|
3852
|
+
field = z3.any();
|
|
3853
|
+
}
|
|
3854
|
+
if (prop.description) {
|
|
3855
|
+
field = field.describe(prop.description);
|
|
3856
|
+
}
|
|
3857
|
+
if (!required.includes(key)) {
|
|
3858
|
+
field = field.optional();
|
|
3859
|
+
}
|
|
3860
|
+
shape[key] = field;
|
|
3861
|
+
}
|
|
3862
|
+
return z3.object(shape);
|
|
3863
|
+
}
|
|
3864
|
+
/**
|
|
3865
|
+
* Returns tools from this MCP server as RadarOS ToolDef[].
|
|
3866
|
+
* Optionally filter by tool names to reduce token usage.
|
|
3867
|
+
*
|
|
3868
|
+
* @param filter - Tool names to include (without the server name prefix).
|
|
3869
|
+
* If omitted, returns all tools.
|
|
3870
|
+
*
|
|
3871
|
+
* @example
|
|
3872
|
+
* // All tools
|
|
3873
|
+
* await mcp.getTools()
|
|
3874
|
+
*
|
|
3875
|
+
* // Only specific tools (pass the original MCP tool names, not prefixed)
|
|
3876
|
+
* await mcp.getTools({ include: ["get_latest_release", "search_repositories"] })
|
|
3877
|
+
*
|
|
3878
|
+
* // Exclude specific tools
|
|
3879
|
+
* await mcp.getTools({ exclude: ["push_files", "create_repository"] })
|
|
3880
|
+
*/
|
|
3881
|
+
async getTools(filter) {
|
|
3882
|
+
if (!this.connected) {
|
|
3883
|
+
await this.connect();
|
|
3884
|
+
}
|
|
3885
|
+
if (!filter) {
|
|
3886
|
+
return [...this.tools];
|
|
3887
|
+
}
|
|
3888
|
+
const prefix = `${this.name}__`;
|
|
3889
|
+
return this.tools.filter((tool) => {
|
|
3890
|
+
const shortName = tool.name.startsWith(prefix) ? tool.name.slice(prefix.length) : tool.name;
|
|
3891
|
+
if (filter.include) {
|
|
3892
|
+
return filter.include.includes(shortName);
|
|
3893
|
+
}
|
|
3894
|
+
if (filter.exclude) {
|
|
3895
|
+
return !filter.exclude.includes(shortName);
|
|
3896
|
+
}
|
|
3897
|
+
return true;
|
|
3898
|
+
});
|
|
3899
|
+
}
|
|
3900
|
+
/** Refresh the tool list from the MCP server. */
|
|
3901
|
+
async refresh() {
|
|
3902
|
+
if (!this.connected) {
|
|
3903
|
+
throw new Error("MCPToolProvider: not connected. Call connect() first.");
|
|
3904
|
+
}
|
|
3905
|
+
await this.discoverTools();
|
|
3906
|
+
}
|
|
3907
|
+
/** Disconnect from the MCP server. */
|
|
3908
|
+
async close() {
|
|
3909
|
+
if (this.client && this.connected) {
|
|
3910
|
+
try {
|
|
3911
|
+
await this.client.close();
|
|
3912
|
+
} catch {
|
|
3913
|
+
}
|
|
3914
|
+
this.connected = false;
|
|
3915
|
+
this.tools = [];
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
};
|
|
3919
|
+
|
|
3920
|
+
// src/a2a/a2a-remote-agent.ts
|
|
3921
|
+
import { z as z2 } from "zod";
|
|
3922
|
+
var A2ARemoteAgent = class {
|
|
3923
|
+
url;
|
|
3924
|
+
name;
|
|
3925
|
+
instructions;
|
|
3926
|
+
skills = [];
|
|
3927
|
+
headers;
|
|
3928
|
+
timeoutMs;
|
|
3929
|
+
card = null;
|
|
3930
|
+
rpcId = 0;
|
|
3931
|
+
get tools() {
|
|
3932
|
+
return [];
|
|
3933
|
+
}
|
|
3934
|
+
get modelId() {
|
|
3935
|
+
return "a2a-remote";
|
|
3936
|
+
}
|
|
3937
|
+
get providerId() {
|
|
3938
|
+
return "a2a";
|
|
3939
|
+
}
|
|
3940
|
+
get hasStructuredOutput() {
|
|
3941
|
+
return false;
|
|
3942
|
+
}
|
|
3943
|
+
constructor(config) {
|
|
3944
|
+
this.url = config.url.replace(/\/$/, "");
|
|
3945
|
+
this.name = config.name ?? "remote-agent";
|
|
3946
|
+
this.instructions = "";
|
|
3947
|
+
this.headers = config.headers ?? {};
|
|
3948
|
+
this.timeoutMs = config.timeoutMs ?? 6e4;
|
|
3949
|
+
}
|
|
3950
|
+
/**
|
|
3951
|
+
* Fetch the Agent Card from /.well-known/agent.json and populate metadata.
|
|
3952
|
+
*/
|
|
3953
|
+
async discover() {
|
|
3954
|
+
const res = await fetch(`${this.url}/.well-known/agent.json`, {
|
|
3955
|
+
headers: this.headers,
|
|
3956
|
+
signal: AbortSignal.timeout(this.timeoutMs)
|
|
3957
|
+
});
|
|
3958
|
+
if (!res.ok) {
|
|
3959
|
+
throw new Error(
|
|
3960
|
+
`A2A discover failed: ${res.status} ${res.statusText} from ${this.url}`
|
|
3961
|
+
);
|
|
3962
|
+
}
|
|
3963
|
+
this.card = await res.json();
|
|
3964
|
+
this.name = this.card.name;
|
|
3965
|
+
this.instructions = this.card.description ?? "";
|
|
3966
|
+
this.skills = this.card.skills?.map((s) => ({
|
|
3967
|
+
id: s.id,
|
|
3968
|
+
name: s.name,
|
|
3969
|
+
description: s.description
|
|
3970
|
+
})) ?? [];
|
|
3971
|
+
return this.card;
|
|
3972
|
+
}
|
|
3973
|
+
/**
|
|
3974
|
+
* Synchronous run: sends message/send and returns RunOutput.
|
|
3975
|
+
*/
|
|
3976
|
+
async run(input, opts) {
|
|
3977
|
+
const message = {
|
|
3978
|
+
role: "user",
|
|
3979
|
+
parts: [{ kind: "text", text: input }]
|
|
3980
|
+
};
|
|
3981
|
+
const rpcReq = {
|
|
3982
|
+
jsonrpc: "2.0",
|
|
3983
|
+
id: ++this.rpcId,
|
|
3984
|
+
method: "message/send",
|
|
3985
|
+
params: {
|
|
3986
|
+
message,
|
|
3987
|
+
...opts?.sessionId ? { sessionId: opts.sessionId } : {}
|
|
3988
|
+
}
|
|
3989
|
+
};
|
|
3990
|
+
const startMs = Date.now();
|
|
3991
|
+
const res = await fetch(this.url, {
|
|
3992
|
+
method: "POST",
|
|
3993
|
+
headers: {
|
|
3994
|
+
"Content-Type": "application/json",
|
|
3995
|
+
...this.headers
|
|
3996
|
+
},
|
|
3997
|
+
body: JSON.stringify(rpcReq),
|
|
3998
|
+
signal: AbortSignal.timeout(this.timeoutMs)
|
|
3999
|
+
});
|
|
4000
|
+
if (!res.ok) {
|
|
4001
|
+
throw new Error(`A2A message/send failed: ${res.status} ${res.statusText}`);
|
|
4002
|
+
}
|
|
4003
|
+
const rpcRes = await res.json();
|
|
4004
|
+
if (rpcRes.error) {
|
|
4005
|
+
throw new Error(`A2A error: ${rpcRes.error.message}`);
|
|
4006
|
+
}
|
|
4007
|
+
const task = rpcRes.result;
|
|
4008
|
+
const agentMsg = task.status?.message;
|
|
4009
|
+
const text = agentMsg ? this.partsToText(agentMsg.parts) : "";
|
|
4010
|
+
return {
|
|
4011
|
+
text,
|
|
4012
|
+
toolCalls: [],
|
|
4013
|
+
usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
|
|
4014
|
+
durationMs: Date.now() - startMs
|
|
4015
|
+
};
|
|
4016
|
+
}
|
|
4017
|
+
/**
|
|
4018
|
+
* Streaming run: sends message/stream and yields StreamChunks from SSE.
|
|
4019
|
+
*/
|
|
4020
|
+
async *stream(input, opts) {
|
|
4021
|
+
const message = {
|
|
4022
|
+
role: "user",
|
|
4023
|
+
parts: [{ kind: "text", text: input }]
|
|
4024
|
+
};
|
|
4025
|
+
const rpcReq = {
|
|
4026
|
+
jsonrpc: "2.0",
|
|
4027
|
+
id: ++this.rpcId,
|
|
4028
|
+
method: "message/stream",
|
|
4029
|
+
params: {
|
|
4030
|
+
message,
|
|
4031
|
+
...opts?.sessionId ? { sessionId: opts.sessionId } : {}
|
|
4032
|
+
}
|
|
4033
|
+
};
|
|
4034
|
+
const res = await fetch(this.url, {
|
|
4035
|
+
method: "POST",
|
|
4036
|
+
headers: {
|
|
4037
|
+
"Content-Type": "application/json",
|
|
4038
|
+
...this.headers
|
|
4039
|
+
},
|
|
4040
|
+
body: JSON.stringify(rpcReq),
|
|
4041
|
+
signal: AbortSignal.timeout(this.timeoutMs)
|
|
4042
|
+
});
|
|
4043
|
+
if (!res.ok) {
|
|
4044
|
+
throw new Error(`A2A message/stream failed: ${res.status} ${res.statusText}`);
|
|
4045
|
+
}
|
|
4046
|
+
if (!res.body) {
|
|
4047
|
+
throw new Error("A2A message/stream: no response body for SSE");
|
|
4048
|
+
}
|
|
4049
|
+
const reader = res.body.getReader();
|
|
4050
|
+
const decoder = new TextDecoder();
|
|
4051
|
+
let buffer = "";
|
|
4052
|
+
while (true) {
|
|
4053
|
+
const { done, value } = await reader.read();
|
|
4054
|
+
if (done) break;
|
|
4055
|
+
buffer += decoder.decode(value, { stream: true });
|
|
4056
|
+
const lines = buffer.split("\n");
|
|
4057
|
+
buffer = lines.pop();
|
|
4058
|
+
for (const line of lines) {
|
|
4059
|
+
if (!line.startsWith("data: ")) continue;
|
|
4060
|
+
const jsonStr = line.slice(6).trim();
|
|
4061
|
+
if (!jsonStr) continue;
|
|
4062
|
+
try {
|
|
4063
|
+
const event = JSON.parse(jsonStr);
|
|
4064
|
+
const task = event.result;
|
|
4065
|
+
if (!task) continue;
|
|
4066
|
+
if (task.status?.state === "working" && task.status.message?.parts?.length) {
|
|
4067
|
+
for (const part of task.status.message.parts) {
|
|
4068
|
+
if (part.kind === "text") {
|
|
4069
|
+
yield { type: "text", text: part.text };
|
|
4070
|
+
}
|
|
4071
|
+
}
|
|
4072
|
+
}
|
|
4073
|
+
if (task.status?.state === "completed") {
|
|
4074
|
+
yield {
|
|
4075
|
+
type: "finish",
|
|
4076
|
+
finishReason: "stop",
|
|
4077
|
+
usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 }
|
|
4078
|
+
};
|
|
4079
|
+
}
|
|
4080
|
+
} catch {
|
|
4081
|
+
}
|
|
4082
|
+
}
|
|
4083
|
+
}
|
|
4084
|
+
}
|
|
4085
|
+
/**
|
|
4086
|
+
* Wrap this remote agent as a ToolDef so it can be used by an orchestrator agent.
|
|
4087
|
+
*/
|
|
4088
|
+
asTool() {
|
|
4089
|
+
const self = this;
|
|
4090
|
+
return {
|
|
4091
|
+
name: `a2a_${this.name.replace(/[^a-zA-Z0-9_]/g, "_")}`,
|
|
4092
|
+
description: this.instructions || `Remote A2A agent: ${this.name}`,
|
|
4093
|
+
parameters: z2.object({
|
|
4094
|
+
message: z2.string().describe("The message to send to the remote agent")
|
|
4095
|
+
}),
|
|
4096
|
+
async execute(args, _ctx) {
|
|
4097
|
+
const result = await self.run(args.message);
|
|
4098
|
+
return result.text;
|
|
4099
|
+
}
|
|
4100
|
+
};
|
|
4101
|
+
}
|
|
4102
|
+
getAgentCard() {
|
|
4103
|
+
return this.card;
|
|
4104
|
+
}
|
|
4105
|
+
partsToText(parts) {
|
|
4106
|
+
return parts.filter(
|
|
4107
|
+
(p) => p.kind === "text"
|
|
4108
|
+
).map((p) => p.text).join("\n");
|
|
4109
|
+
}
|
|
4110
|
+
};
|
|
3425
4111
|
export {
|
|
4112
|
+
A2ARemoteAgent,
|
|
3426
4113
|
Agent,
|
|
3427
4114
|
AnthropicProvider,
|
|
3428
4115
|
BaseVectorStore,
|
|
@@ -3434,6 +4121,7 @@ export {
|
|
|
3434
4121
|
KnowledgeBase,
|
|
3435
4122
|
LLMLoop,
|
|
3436
4123
|
Logger,
|
|
4124
|
+
MCPToolProvider,
|
|
3437
4125
|
Memory,
|
|
3438
4126
|
ModelRegistry,
|
|
3439
4127
|
MongoDBStorage,
|
|
@@ -3450,6 +4138,7 @@ export {
|
|
|
3450
4138
|
Team,
|
|
3451
4139
|
TeamMode,
|
|
3452
4140
|
ToolExecutor,
|
|
4141
|
+
VertexAIProvider,
|
|
3453
4142
|
Workflow,
|
|
3454
4143
|
anthropic,
|
|
3455
4144
|
defineTool,
|
|
@@ -3458,5 +4147,6 @@ export {
|
|
|
3458
4147
|
isMultiModal,
|
|
3459
4148
|
ollama,
|
|
3460
4149
|
openai,
|
|
3461
|
-
registry
|
|
4150
|
+
registry,
|
|
4151
|
+
vertex
|
|
3462
4152
|
};
|