@anvia/studio 0.5.14 → 0.6.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/README.md +62 -0
- package/dist/index.d.ts +78 -2
- package/dist/index.js +506 -9
- package/dist/index.js.map +1 -1
- package/dist/ui/assets/{ArrowClockwise.es-cF7OPZy5.js → ArrowClockwise.es-BA79KX3F.js} +2 -2
- package/dist/ui/assets/{ArrowClockwise.es-cF7OPZy5.js.map → ArrowClockwise.es-BA79KX3F.js.map} +1 -1
- package/dist/ui/assets/{Path.es-D-NB-Xon.js → Path.es-DTZ3zsLn.js} +2 -2
- package/dist/ui/assets/{Path.es-D-NB-Xon.js.map → Path.es-DTZ3zsLn.js.map} +1 -1
- package/dist/ui/assets/{Play.es-BoMnJL28.js → Play.es-CJTRfv9R.js} +2 -2
- package/dist/ui/assets/{Play.es-BoMnJL28.js.map → Play.es-CJTRfv9R.js.map} +1 -1
- package/dist/ui/assets/{evals-page-Cx4Ycr-Y.js → evals-page-rS-P7eLF.js} +2 -2
- package/dist/ui/assets/{evals-page-Cx4Ycr-Y.js.map → evals-page-rS-P7eLF.js.map} +1 -1
- package/dist/ui/assets/index-B1s3oC-V.css +1 -0
- package/dist/ui/assets/index-ChlKOnbD.js +54 -0
- package/dist/ui/assets/index-ChlKOnbD.js.map +1 -0
- package/dist/ui/assets/{knowledge-page-DASnGzYP.js → knowledge-page-CR0JUneq.js} +2 -2
- package/dist/ui/assets/{knowledge-page-DASnGzYP.js.map → knowledge-page-CR0JUneq.js.map} +1 -1
- package/dist/ui/assets/{mcps-page-B7VAqsoU.js → mcps-page-D3P0UAWf.js} +2 -2
- package/dist/ui/assets/{mcps-page-B7VAqsoU.js.map → mcps-page-D3P0UAWf.js.map} +1 -1
- package/dist/ui/assets/{memory-page-BCv19Tw_.js → memory-page-kVDqOAOd.js} +2 -2
- package/dist/ui/assets/{memory-page-BCv19Tw_.js.map → memory-page-kVDqOAOd.js.map} +1 -1
- package/dist/ui/assets/{pipelines-page-Byb_JYxi.js → pipelines-page-lMfsVNRy.js} +2 -2
- package/dist/ui/assets/{pipelines-page-Byb_JYxi.js.map → pipelines-page-lMfsVNRy.js.map} +1 -1
- package/dist/ui/assets/{renderers-C8RHOlPz.js → renderers-Bc89UGTN.js} +2 -2
- package/dist/ui/assets/{renderers-C8RHOlPz.js.map → renderers-Bc89UGTN.js.map} +1 -1
- package/dist/ui/assets/{sessions-page-CV8GZKjN.js → sessions-page-CVlkRPfT.js} +2 -2
- package/dist/ui/assets/{sessions-page-CV8GZKjN.js.map → sessions-page-CVlkRPfT.js.map} +1 -1
- package/dist/ui/assets/{status-page-a6SMAcLu.js → status-page-XKvTGruQ.js} +2 -2
- package/dist/ui/assets/{status-page-a6SMAcLu.js.map → status-page-XKvTGruQ.js.map} +1 -1
- package/dist/ui/assets/{tools-page-Dpj7hI8q.js → tools-page-DJt_QdDe.js} +2 -2
- package/dist/ui/assets/{tools-page-Dpj7hI8q.js.map → tools-page-DJt_QdDe.js.map} +1 -1
- package/dist/ui/assets/{trace-browser-dvGDi7ji.js → trace-browser-BBITQELO.js} +2 -2
- package/dist/ui/assets/{trace-browser-dvGDi7ji.js.map → trace-browser-BBITQELO.js.map} +1 -1
- package/dist/ui/assets/{transcript-item-GmOQdc53.js → transcript-item-BvaA1PBu.js} +3 -3
- package/dist/ui/assets/transcript-item-BvaA1PBu.js.map +1 -0
- package/dist/ui/index.html +2 -2
- package/package.json +1 -1
- package/dist/ui/assets/index-BBfu6qlV.css +0 -1
- package/dist/ui/assets/index-rc2RWnWD.js +0 -54
- package/dist/ui/assets/index-rc2RWnWD.js.map +0 -1
- package/dist/ui/assets/transcript-item-GmOQdc53.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -835,16 +835,29 @@ function transcriptFromMessages(messages) {
|
|
|
835
835
|
continue;
|
|
836
836
|
}
|
|
837
837
|
if (message.role === "user") {
|
|
838
|
+
const attachments = attachmentsFromMessage(message);
|
|
839
|
+
let textEntryAdded = false;
|
|
838
840
|
for (const content of message.content) {
|
|
839
841
|
if (content.type === "text") {
|
|
840
842
|
transcript.push({
|
|
841
843
|
entryId: transcript.length,
|
|
842
844
|
kind: "message",
|
|
843
845
|
role: "user",
|
|
844
|
-
text: content.text
|
|
846
|
+
text: content.text,
|
|
847
|
+
...attachments.length === 0 ? {} : { attachments }
|
|
845
848
|
});
|
|
849
|
+
textEntryAdded = true;
|
|
846
850
|
}
|
|
847
851
|
}
|
|
852
|
+
if (!textEntryAdded && attachments.length > 0) {
|
|
853
|
+
transcript.push({
|
|
854
|
+
entryId: transcript.length,
|
|
855
|
+
kind: "message",
|
|
856
|
+
role: "user",
|
|
857
|
+
text: "",
|
|
858
|
+
attachments
|
|
859
|
+
});
|
|
860
|
+
}
|
|
848
861
|
continue;
|
|
849
862
|
}
|
|
850
863
|
if (message.role === "tool") {
|
|
@@ -885,6 +898,32 @@ function transcriptFromMessages(messages) {
|
|
|
885
898
|
}
|
|
886
899
|
return transcript;
|
|
887
900
|
}
|
|
901
|
+
function attachmentsFromMessage(message) {
|
|
902
|
+
if (message.role !== "user" && message.role !== "assistant") {
|
|
903
|
+
return [];
|
|
904
|
+
}
|
|
905
|
+
return message.content.flatMap((content) => {
|
|
906
|
+
if (content.type === "image") {
|
|
907
|
+
return [
|
|
908
|
+
{
|
|
909
|
+
kind: "image",
|
|
910
|
+
...content.source.type === "base64" ? { data: content.source.data, mediaType: content.source.mediaType } : { url: content.source.url }
|
|
911
|
+
}
|
|
912
|
+
];
|
|
913
|
+
}
|
|
914
|
+
if (content.type === "document") {
|
|
915
|
+
return [
|
|
916
|
+
{
|
|
917
|
+
kind: "document",
|
|
918
|
+
...content.source.filename === void 0 ? {} : { name: content.source.filename },
|
|
919
|
+
...content.source.mediaType === void 0 ? {} : { mediaType: content.source.mediaType },
|
|
920
|
+
...content.source.type === "base64" ? { data: content.source.data } : content.source.type === "url" ? { url: content.source.url } : {}
|
|
921
|
+
}
|
|
922
|
+
];
|
|
923
|
+
}
|
|
924
|
+
return [];
|
|
925
|
+
});
|
|
926
|
+
}
|
|
888
927
|
function appendAssistantTranscriptText(transcript, text) {
|
|
889
928
|
const last = transcript.at(-1);
|
|
890
929
|
if (last?.kind === "message" && last.role === "assistant") {
|
|
@@ -940,6 +979,19 @@ var InMemoryStudioStore = class {
|
|
|
940
979
|
const session = this.sessions.get(id);
|
|
941
980
|
return session === void 0 ? void 0 : materializeSession(session);
|
|
942
981
|
}
|
|
982
|
+
updateSessionMetadata(id, metadata) {
|
|
983
|
+
const session = this.sessions.get(id);
|
|
984
|
+
if (session === void 0) {
|
|
985
|
+
return void 0;
|
|
986
|
+
}
|
|
987
|
+
if (metadata === void 0) {
|
|
988
|
+
delete session.metadata;
|
|
989
|
+
} else {
|
|
990
|
+
session.metadata = metadata;
|
|
991
|
+
}
|
|
992
|
+
session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
993
|
+
return materializeSession(session);
|
|
994
|
+
}
|
|
943
995
|
load(context) {
|
|
944
996
|
return Promise.resolve(this.sessions.get(context.sessionId)?.messages ?? []);
|
|
945
997
|
}
|
|
@@ -1145,6 +1197,342 @@ function isJsonValue(value) {
|
|
|
1145
1197
|
return isJsonObject(value) && Object.values(value).every(isJsonValue);
|
|
1146
1198
|
}
|
|
1147
1199
|
|
|
1200
|
+
// src/runtime/models.ts
|
|
1201
|
+
var STUDIO_MODEL_METADATA_KEY = "studioModel";
|
|
1202
|
+
function createStudioModelRegistry(config) {
|
|
1203
|
+
if (config === void 0) {
|
|
1204
|
+
return void 0;
|
|
1205
|
+
}
|
|
1206
|
+
const providers = /* @__PURE__ */ new Map();
|
|
1207
|
+
for (const provider of config.providers) {
|
|
1208
|
+
const id = normalizeProviderId(provider.id);
|
|
1209
|
+
if (providers.has(id)) {
|
|
1210
|
+
throw new Error(`Duplicate Studio model provider id: ${id}`);
|
|
1211
|
+
}
|
|
1212
|
+
const staticModels = /* @__PURE__ */ new Map();
|
|
1213
|
+
for (const model of provider.models ?? []) {
|
|
1214
|
+
const modelId = normalizeModelId(model.id);
|
|
1215
|
+
if (staticModels.has(modelId)) {
|
|
1216
|
+
throw new Error(`Duplicate Studio model id for provider ${id}: ${modelId}`);
|
|
1217
|
+
}
|
|
1218
|
+
staticModels.set(modelId, { ...model, id: modelId });
|
|
1219
|
+
}
|
|
1220
|
+
providers.set(id, {
|
|
1221
|
+
...provider,
|
|
1222
|
+
id,
|
|
1223
|
+
...provider.defaultModel === void 0 ? {} : { defaultModel: normalizeModelId(provider.defaultModel) },
|
|
1224
|
+
staticModels
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
return {
|
|
1228
|
+
...config.default === void 0 ? {} : { defaultModel: normalizeModelRef(config.default) },
|
|
1229
|
+
providers,
|
|
1230
|
+
agentPolicies: normalizeAgentPolicies(config.agents ?? {}),
|
|
1231
|
+
modelCache: /* @__PURE__ */ new Map()
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
function studioModelsConfig(registry, agents) {
|
|
1235
|
+
if (registry === void 0) {
|
|
1236
|
+
return void 0;
|
|
1237
|
+
}
|
|
1238
|
+
const providers = [...registry.providers.values()].map((provider) => {
|
|
1239
|
+
const models = [...provider.staticModels.values()].map(
|
|
1240
|
+
(model) => modelSummary(provider, model.id, model)
|
|
1241
|
+
);
|
|
1242
|
+
return {
|
|
1243
|
+
id: provider.id,
|
|
1244
|
+
...provider.name === void 0 ? {} : { name: provider.name },
|
|
1245
|
+
...provider.defaultModel === void 0 ? {} : { defaultModel: provider.defaultModel },
|
|
1246
|
+
models,
|
|
1247
|
+
...provider.metadata === void 0 ? {} : { metadata: provider.metadata }
|
|
1248
|
+
};
|
|
1249
|
+
});
|
|
1250
|
+
const agentIds = new Set(agents.map((agent) => agent.id));
|
|
1251
|
+
const agentsConfig = Object.fromEntries(
|
|
1252
|
+
[...registry.agentPolicies.entries()].filter(([agentId]) => agentIds.has(agentId)).map(([agentId, policy]) => [agentId, publicPolicy(policy)])
|
|
1253
|
+
);
|
|
1254
|
+
return {
|
|
1255
|
+
providers,
|
|
1256
|
+
...registry.defaultModel === void 0 ? {} : { default: registry.defaultModel },
|
|
1257
|
+
agents: agentsConfig
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1260
|
+
function registerModelRoutes(app, props) {
|
|
1261
|
+
app.get("/models", async (c) => {
|
|
1262
|
+
if (props.registry === void 0) {
|
|
1263
|
+
return errorResponse(c, 404, "not_found", "Model registry not configured");
|
|
1264
|
+
}
|
|
1265
|
+
const providers = await Promise.all(
|
|
1266
|
+
[...props.registry.providers.values()].map((provider) => providerCatalog(provider))
|
|
1267
|
+
);
|
|
1268
|
+
return c.json({
|
|
1269
|
+
providers,
|
|
1270
|
+
...props.registry.defaultModel === void 0 ? {} : { defaultModel: props.registry.defaultModel }
|
|
1271
|
+
});
|
|
1272
|
+
});
|
|
1273
|
+
app.get("/models/:providerId", async (c) => {
|
|
1274
|
+
if (props.registry === void 0) {
|
|
1275
|
+
return errorResponse(c, 404, "not_found", "Model registry not configured");
|
|
1276
|
+
}
|
|
1277
|
+
const provider = props.registry.providers.get(c.req.param("providerId"));
|
|
1278
|
+
if (provider === void 0) {
|
|
1279
|
+
return errorResponse(c, 404, "not_found", "Model provider not found");
|
|
1280
|
+
}
|
|
1281
|
+
return c.json(await providerCatalog(provider));
|
|
1282
|
+
});
|
|
1283
|
+
app.get("/agents/:agentId/models", async (c) => {
|
|
1284
|
+
const agentId = c.req.param("agentId");
|
|
1285
|
+
const agent = props.agentMap.get(agentId);
|
|
1286
|
+
if (agent === void 0) {
|
|
1287
|
+
return errorResponse(c, 404, "not_found", "Agent not found");
|
|
1288
|
+
}
|
|
1289
|
+
if (props.registry === void 0) {
|
|
1290
|
+
return c.json({
|
|
1291
|
+
agentId,
|
|
1292
|
+
models: []
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
return c.json(await agentModelsCatalog(props.registry, agent));
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1298
|
+
function resolveStudioModel(registry, input) {
|
|
1299
|
+
if (registry === void 0) {
|
|
1300
|
+
return { warnings: [] };
|
|
1301
|
+
}
|
|
1302
|
+
const selectedRef = normalizeOptionalModelRef(input.request.model) ?? sessionModelRef(input.sessionMetadata) ?? registry.agentPolicies.get(input.agent.id)?.default ?? registry.defaultModel;
|
|
1303
|
+
if (selectedRef === void 0) {
|
|
1304
|
+
return { warnings: [] };
|
|
1305
|
+
}
|
|
1306
|
+
ensureModelAllowed(registry, input.agent.id, selectedRef);
|
|
1307
|
+
const { providerId, modelId } = parseModelRef(selectedRef);
|
|
1308
|
+
const provider = registry.providers.get(providerId);
|
|
1309
|
+
if (provider === void 0) {
|
|
1310
|
+
throw new ModelSelectionError(`Unknown model provider: ${providerId}`);
|
|
1311
|
+
}
|
|
1312
|
+
let model = registry.modelCache.get(selectedRef);
|
|
1313
|
+
if (model === void 0) {
|
|
1314
|
+
model = provider.createCompletionModel(modelId);
|
|
1315
|
+
registry.modelCache.set(selectedRef, model);
|
|
1316
|
+
}
|
|
1317
|
+
const metadata = provider.staticModels.get(modelId);
|
|
1318
|
+
return {
|
|
1319
|
+
ref: selectedRef,
|
|
1320
|
+
model,
|
|
1321
|
+
warnings: modelWarnings(selectedRef, metadata, input.request)
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1324
|
+
function sessionModelRef(metadata) {
|
|
1325
|
+
const value = metadata?.[STUDIO_MODEL_METADATA_KEY];
|
|
1326
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
1327
|
+
}
|
|
1328
|
+
function normalizeOptionalModelRef(ref) {
|
|
1329
|
+
return ref === void 0 ? void 0 : normalizeModelRef(ref);
|
|
1330
|
+
}
|
|
1331
|
+
function normalizeModelRef(ref) {
|
|
1332
|
+
if (typeof ref === "string") {
|
|
1333
|
+
const trimmed = ref.trim();
|
|
1334
|
+
const parsed = parseModelRef(trimmed);
|
|
1335
|
+
return `${parsed.providerId}:${parsed.modelId}`;
|
|
1336
|
+
}
|
|
1337
|
+
return `${normalizeProviderId(ref.provider)}:${normalizeModelId(ref.model)}`;
|
|
1338
|
+
}
|
|
1339
|
+
function parseModelRef(ref) {
|
|
1340
|
+
const index = ref.indexOf(":");
|
|
1341
|
+
if (index <= 0 || index === ref.length - 1) {
|
|
1342
|
+
throw new ModelSelectionError(`Model ref must use provider:model format: ${ref}`);
|
|
1343
|
+
}
|
|
1344
|
+
return {
|
|
1345
|
+
providerId: normalizeProviderId(ref.slice(0, index)),
|
|
1346
|
+
modelId: normalizeModelId(ref.slice(index + 1))
|
|
1347
|
+
};
|
|
1348
|
+
}
|
|
1349
|
+
var ModelSelectionError = class extends Error {
|
|
1350
|
+
constructor(message) {
|
|
1351
|
+
super(message);
|
|
1352
|
+
this.name = "ModelSelectionError";
|
|
1353
|
+
}
|
|
1354
|
+
};
|
|
1355
|
+
async function agentModelsCatalog(registry, agent) {
|
|
1356
|
+
const policy = registry.agentPolicies.get(agent.id);
|
|
1357
|
+
const catalogs = await Promise.all([...registry.providers.values()].map(providerCatalog));
|
|
1358
|
+
const warnings = catalogs.flatMap(
|
|
1359
|
+
(catalog) => catalog.warning === void 0 ? [] : [{ providerId: catalog.id, warning: catalog.warning }]
|
|
1360
|
+
);
|
|
1361
|
+
const models = catalogs.flatMap((catalog) => catalog.models).filter((model) => {
|
|
1362
|
+
if (policy?.allowed === void 0) {
|
|
1363
|
+
return true;
|
|
1364
|
+
}
|
|
1365
|
+
return allowedByPolicy(policy.allowed, model.ref);
|
|
1366
|
+
});
|
|
1367
|
+
const exactPolicyModels = (policy?.allowed ?? []).filter((allowed) => !allowed.endsWith(":*")).filter((allowed) => models.every((model) => model.ref !== allowed)).flatMap((ref) => {
|
|
1368
|
+
try {
|
|
1369
|
+
const { providerId, modelId } = parseModelRef(ref);
|
|
1370
|
+
const provider = registry.providers.get(providerId);
|
|
1371
|
+
return provider === void 0 ? [] : [
|
|
1372
|
+
modelSummary(provider, modelId, {
|
|
1373
|
+
id: modelId
|
|
1374
|
+
})
|
|
1375
|
+
];
|
|
1376
|
+
} catch {
|
|
1377
|
+
return [];
|
|
1378
|
+
}
|
|
1379
|
+
});
|
|
1380
|
+
const defaultModel = policy?.default ?? registry.defaultModel;
|
|
1381
|
+
return {
|
|
1382
|
+
agentId: agent.id,
|
|
1383
|
+
...defaultModel === void 0 ? {} : { defaultModel },
|
|
1384
|
+
models: [...models, ...exactPolicyModels],
|
|
1385
|
+
...warnings.length === 0 ? {} : { warnings }
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
1388
|
+
async function providerCatalog(provider) {
|
|
1389
|
+
const models = /* @__PURE__ */ new Map();
|
|
1390
|
+
for (const model of provider.staticModels.values()) {
|
|
1391
|
+
models.set(model.id, modelSummary(provider, model.id, model));
|
|
1392
|
+
}
|
|
1393
|
+
let warning;
|
|
1394
|
+
if (provider.listModels !== void 0) {
|
|
1395
|
+
try {
|
|
1396
|
+
const listed = await provider.listModels();
|
|
1397
|
+
for (const model of listed.data) {
|
|
1398
|
+
const staticModel = provider.staticModels.get(model.id);
|
|
1399
|
+
models.set(
|
|
1400
|
+
model.id,
|
|
1401
|
+
modelSummary(provider, model.id, {
|
|
1402
|
+
id: model.id,
|
|
1403
|
+
...model.name === void 0 ? {} : { name: model.name },
|
|
1404
|
+
...model.description === void 0 ? {} : { description: model.description },
|
|
1405
|
+
...staticModel ?? {},
|
|
1406
|
+
metadata: {
|
|
1407
|
+
...model.type === void 0 ? {} : { type: model.type },
|
|
1408
|
+
...model.createdAt === void 0 ? {} : { createdAt: model.createdAt },
|
|
1409
|
+
...model.ownedBy === void 0 ? {} : { ownedBy: model.ownedBy },
|
|
1410
|
+
...model.contextLength === void 0 ? {} : { contextLength: model.contextLength },
|
|
1411
|
+
...staticModel?.metadata ?? {}
|
|
1412
|
+
}
|
|
1413
|
+
})
|
|
1414
|
+
);
|
|
1415
|
+
}
|
|
1416
|
+
} catch (error) {
|
|
1417
|
+
const serialized = serializeError(error);
|
|
1418
|
+
warning = typeof serialized === "object" && serialized !== null && "message" in serialized ? String(serialized.message) : String(error);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
return {
|
|
1422
|
+
id: provider.id,
|
|
1423
|
+
...provider.name === void 0 ? {} : { name: provider.name },
|
|
1424
|
+
...provider.defaultModel === void 0 ? {} : { defaultModel: provider.defaultModel },
|
|
1425
|
+
models: [...models.values()].sort((left, right) => left.ref.localeCompare(right.ref)),
|
|
1426
|
+
...provider.metadata === void 0 ? {} : { metadata: provider.metadata },
|
|
1427
|
+
...warning === void 0 ? {} : { warning }
|
|
1428
|
+
};
|
|
1429
|
+
}
|
|
1430
|
+
function modelSummary(provider, modelId, model) {
|
|
1431
|
+
return {
|
|
1432
|
+
...model,
|
|
1433
|
+
id: modelId,
|
|
1434
|
+
ref: `${provider.id}:${modelId}`,
|
|
1435
|
+
providerId: provider.id,
|
|
1436
|
+
...provider.name === void 0 ? {} : { providerName: provider.name }
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
1439
|
+
function ensureModelAllowed(registry, agentId, ref) {
|
|
1440
|
+
const { providerId } = parseModelRef(ref);
|
|
1441
|
+
if (!registry.providers.has(providerId)) {
|
|
1442
|
+
throw new ModelSelectionError(`Unknown model provider: ${providerId}`);
|
|
1443
|
+
}
|
|
1444
|
+
const policy = registry.agentPolicies.get(agentId);
|
|
1445
|
+
if (policy?.allowed !== void 0 && !allowedByPolicy(policy.allowed, ref)) {
|
|
1446
|
+
throw new ModelSelectionError(`Model ${ref} is not allowed for agent ${agentId}`);
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
function allowedByPolicy(allowed, ref) {
|
|
1450
|
+
return allowed.some(
|
|
1451
|
+
(entry) => entry.endsWith(":*") ? ref.startsWith(entry.slice(0, -1)) : entry === ref
|
|
1452
|
+
);
|
|
1453
|
+
}
|
|
1454
|
+
function normalizeAgentPolicies(policies) {
|
|
1455
|
+
return new Map(
|
|
1456
|
+
Object.entries(policies).map(([agentId, policy]) => [
|
|
1457
|
+
agentId.trim(),
|
|
1458
|
+
{
|
|
1459
|
+
...policy.default === void 0 ? {} : { default: normalizeModelRef(policy.default) },
|
|
1460
|
+
...policy.allowed === void 0 ? {} : {
|
|
1461
|
+
allowed: policy.allowed.map(
|
|
1462
|
+
(entry) => typeof entry === "string" && entry.trim().endsWith(":*") ? `${normalizeProviderId(entry.trim().slice(0, -2))}:*` : normalizeModelRef(entry)
|
|
1463
|
+
)
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
])
|
|
1467
|
+
);
|
|
1468
|
+
}
|
|
1469
|
+
function publicPolicy(policy) {
|
|
1470
|
+
return {
|
|
1471
|
+
...policy.default === void 0 ? {} : { default: policy.default },
|
|
1472
|
+
...policy.allowed === void 0 ? {} : { allowed: policy.allowed }
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
function modelWarnings(ref, model, request) {
|
|
1476
|
+
const warnings = [];
|
|
1477
|
+
const modalities = requestModalities(request);
|
|
1478
|
+
const missingModalities = [...modalities].filter(
|
|
1479
|
+
(modality) => !model?.modalities?.input.includes(modality)
|
|
1480
|
+
);
|
|
1481
|
+
if (model?.modalities !== void 0 && missingModalities.length > 0) {
|
|
1482
|
+
warnings.push({
|
|
1483
|
+
model: ref,
|
|
1484
|
+
kind: "modality",
|
|
1485
|
+
message: `Model ${ref} does not declare input support for ${missingModalities.join(", ")}`,
|
|
1486
|
+
missing: missingModalities
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1489
|
+
if (request.stream === true && model?.capabilities?.streaming === false) {
|
|
1490
|
+
warnings.push({
|
|
1491
|
+
model: ref,
|
|
1492
|
+
kind: "capability",
|
|
1493
|
+
message: `Model ${ref} is configured as non-streaming but the run requested streaming`,
|
|
1494
|
+
capability: "streaming"
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1497
|
+
return warnings;
|
|
1498
|
+
}
|
|
1499
|
+
function requestModalities(request) {
|
|
1500
|
+
const modalities = /* @__PURE__ */ new Set(["text"]);
|
|
1501
|
+
for (const message of requestMessages(request)) {
|
|
1502
|
+
if (typeof message === "string") {
|
|
1503
|
+
continue;
|
|
1504
|
+
}
|
|
1505
|
+
if (message.role === "user" || message.role === "assistant") {
|
|
1506
|
+
for (const content of message.content) {
|
|
1507
|
+
if (content.type === "image") {
|
|
1508
|
+
modalities.add("image");
|
|
1509
|
+
}
|
|
1510
|
+
if (content.type === "document") {
|
|
1511
|
+
modalities.add("document");
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
return modalities;
|
|
1517
|
+
}
|
|
1518
|
+
function requestMessages(request) {
|
|
1519
|
+
return [...request.history ?? [], request.message];
|
|
1520
|
+
}
|
|
1521
|
+
function normalizeProviderId(id) {
|
|
1522
|
+
const trimmed = id.trim();
|
|
1523
|
+
if (trimmed.length === 0 || trimmed.includes(":")) {
|
|
1524
|
+
throw new ModelSelectionError("Model provider id must be non-empty and cannot contain ':'");
|
|
1525
|
+
}
|
|
1526
|
+
return trimmed;
|
|
1527
|
+
}
|
|
1528
|
+
function normalizeModelId(id) {
|
|
1529
|
+
const trimmed = id.trim();
|
|
1530
|
+
if (trimmed.length === 0) {
|
|
1531
|
+
throw new ModelSelectionError("Model id cannot be empty");
|
|
1532
|
+
}
|
|
1533
|
+
return trimmed;
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1148
1536
|
// src/runtime/tool-metadata.ts
|
|
1149
1537
|
import { ToolSet } from "@anvia/core/tool";
|
|
1150
1538
|
var MCP_TOOL_METADATA_KEY = /* @__PURE__ */ Symbol.for("anvia.mcp.tool.metadata");
|
|
@@ -1296,12 +1684,14 @@ function normalizePipelines(pipelines) {
|
|
|
1296
1684
|
});
|
|
1297
1685
|
}
|
|
1298
1686
|
function buildConfig(options, agents, pipelines, stores) {
|
|
1687
|
+
const models = options.models === void 0 ? void 0 : studioModelsConfig(createStudioModelRegistry(options.models), agents);
|
|
1299
1688
|
return {
|
|
1300
1689
|
id: runnerId(options),
|
|
1301
1690
|
...options.name === void 0 ? {} : { name: options.name },
|
|
1302
1691
|
...options.description === void 0 ? {} : { description: options.description },
|
|
1303
1692
|
...options.version === void 0 ? {} : { version: options.version },
|
|
1304
1693
|
agents: agents.map(agentConfig),
|
|
1694
|
+
...models === void 0 ? {} : { models },
|
|
1305
1695
|
pipelines: pipelines.map(pipelineConfig),
|
|
1306
1696
|
evals: options.evals.map(evalConfig),
|
|
1307
1697
|
chat: {
|
|
@@ -3097,7 +3487,8 @@ function messageToTranscriptEntry(message, entryId) {
|
|
|
3097
3487
|
entryId,
|
|
3098
3488
|
kind: "message",
|
|
3099
3489
|
role,
|
|
3100
|
-
text: extractMessageText(message)
|
|
3490
|
+
text: extractMessageText(message),
|
|
3491
|
+
...role === "user" ? optionalTranscriptAttachments(message) : {}
|
|
3101
3492
|
};
|
|
3102
3493
|
}
|
|
3103
3494
|
function appendTranscriptAssistantText(transcript, delta) {
|
|
@@ -3193,6 +3584,33 @@ function extractMessageText(message) {
|
|
|
3193
3584
|
return [];
|
|
3194
3585
|
}).join("\n");
|
|
3195
3586
|
}
|
|
3587
|
+
function optionalTranscriptAttachments(message) {
|
|
3588
|
+
if (typeof message === "string" || message.role !== "user") {
|
|
3589
|
+
return {};
|
|
3590
|
+
}
|
|
3591
|
+
const attachments = message.content.flatMap((content) => {
|
|
3592
|
+
if (content.type === "image") {
|
|
3593
|
+
return [
|
|
3594
|
+
{
|
|
3595
|
+
kind: "image",
|
|
3596
|
+
...content.source.type === "base64" ? { data: content.source.data, mediaType: content.source.mediaType } : { url: content.source.url }
|
|
3597
|
+
}
|
|
3598
|
+
];
|
|
3599
|
+
}
|
|
3600
|
+
if (content.type === "document") {
|
|
3601
|
+
return [
|
|
3602
|
+
{
|
|
3603
|
+
kind: "document",
|
|
3604
|
+
...content.source.filename === void 0 ? {} : { name: content.source.filename },
|
|
3605
|
+
...content.source.mediaType === void 0 ? {} : { mediaType: content.source.mediaType },
|
|
3606
|
+
...content.source.type === "base64" ? { data: content.source.data } : content.source.type === "url" ? { url: content.source.url } : {}
|
|
3607
|
+
}
|
|
3608
|
+
];
|
|
3609
|
+
}
|
|
3610
|
+
return [];
|
|
3611
|
+
});
|
|
3612
|
+
return attachments.length === 0 ? {} : { attachments };
|
|
3613
|
+
}
|
|
3196
3614
|
function formatJson2(value) {
|
|
3197
3615
|
try {
|
|
3198
3616
|
return JSON.stringify(value, null, 2);
|
|
@@ -3257,6 +3675,25 @@ async function parseRunRequest(c) {
|
|
|
3257
3675
|
}
|
|
3258
3676
|
request.toolConcurrency = body.toolConcurrency;
|
|
3259
3677
|
}
|
|
3678
|
+
if ("model" in body) {
|
|
3679
|
+
if (typeof body.model === "string") {
|
|
3680
|
+
request.model = body.model;
|
|
3681
|
+
} else if (isObject(body.model) && typeof body.model.provider === "string" && typeof body.model.model === "string") {
|
|
3682
|
+
request.model = {
|
|
3683
|
+
provider: body.model.provider,
|
|
3684
|
+
model: body.model.model
|
|
3685
|
+
};
|
|
3686
|
+
} else {
|
|
3687
|
+
return {
|
|
3688
|
+
error: errorResponse(
|
|
3689
|
+
c,
|
|
3690
|
+
400,
|
|
3691
|
+
"bad_request",
|
|
3692
|
+
"model must be a provider:model string or { provider, model } object"
|
|
3693
|
+
)
|
|
3694
|
+
};
|
|
3695
|
+
}
|
|
3696
|
+
}
|
|
3260
3697
|
if ("metadata" in body) {
|
|
3261
3698
|
if (!isJsonObject2(body.metadata)) {
|
|
3262
3699
|
return { error: errorResponse(c, 400, "bad_request", "metadata must be an object") };
|
|
@@ -4825,6 +5262,7 @@ function studioOptionsFromTargets(targets, options) {
|
|
|
4825
5262
|
agents: inferStudioAgents(agents, options.quickPrompts ?? {}),
|
|
4826
5263
|
pipelines: inferStudioPipelines(pipelines),
|
|
4827
5264
|
evals: options.evals ?? [],
|
|
5265
|
+
...options.models === void 0 ? {} : { models: options.models },
|
|
4828
5266
|
...options.stores === void 0 ? {} : { stores: options.stores },
|
|
4829
5267
|
...options.ui === void 0 ? {} : { ui: options.ui }
|
|
4830
5268
|
};
|
|
@@ -4882,6 +5320,7 @@ function createStudioApp(options) {
|
|
|
4882
5320
|
const agents = normalizeAgents(options.agents).map(
|
|
4883
5321
|
(agent) => withStudioTraceObserver(agent, stores.traces)
|
|
4884
5322
|
);
|
|
5323
|
+
const modelRegistry = createStudioModelRegistry(options.models);
|
|
4885
5324
|
const pipelines = normalizePipelines(options.pipelines);
|
|
4886
5325
|
const agentMap = new Map(agents.map((agent) => [agent.id, agent]));
|
|
4887
5326
|
const pipelineMap = new Map(pipelines.map((pipeline) => [pipeline.id, pipeline]));
|
|
@@ -4924,6 +5363,7 @@ function createStudioApp(options) {
|
|
|
4924
5363
|
}
|
|
4925
5364
|
return c.json(agentRuntimeSummary(agent));
|
|
4926
5365
|
});
|
|
5366
|
+
registerModelRoutes(app, { registry: modelRegistry, agentMap });
|
|
4927
5367
|
registerMcpRoutes(app, { agentMap });
|
|
4928
5368
|
registerToolRoutes(app, { agentMap });
|
|
4929
5369
|
registerApprovalRoutes(app, approvalRuntime);
|
|
@@ -4963,6 +5403,20 @@ function createStudioApp(options) {
|
|
|
4963
5403
|
if (session !== void 0 && session.agentId !== agentId) {
|
|
4964
5404
|
return errorResponse(c, 400, "bad_request", "Session belongs to another agent");
|
|
4965
5405
|
}
|
|
5406
|
+
let selectedModel;
|
|
5407
|
+
try {
|
|
5408
|
+
selectedModel = resolveStudioModel(modelRegistry, {
|
|
5409
|
+
agent,
|
|
5410
|
+
request: body,
|
|
5411
|
+
sessionMetadata: session?.metadata
|
|
5412
|
+
});
|
|
5413
|
+
} catch (error) {
|
|
5414
|
+
if (error instanceof ModelSelectionError) {
|
|
5415
|
+
return errorResponse(c, 400, "bad_request", error.message);
|
|
5416
|
+
}
|
|
5417
|
+
throw error;
|
|
5418
|
+
}
|
|
5419
|
+
const runAgent = selectedModel.model === void 0 ? agent.agent : cloneAgent(agent.agent, { model: selectedModel.model });
|
|
4966
5420
|
const runId = globalThis.crypto.randomUUID();
|
|
4967
5421
|
const runStartedAt = Date.now();
|
|
4968
5422
|
if (session !== void 0) {
|
|
@@ -4977,18 +5431,43 @@ function createStudioApp(options) {
|
|
|
4977
5431
|
...body.maxTurns === void 0 ? {} : { maxTurns: body.maxTurns },
|
|
4978
5432
|
...body.toolConcurrency === void 0 ? {} : { toolConcurrency: body.toolConcurrency },
|
|
4979
5433
|
hasTrace: body.trace !== void 0,
|
|
4980
|
-
...body.metadata === void 0 ? {} : {
|
|
5434
|
+
...body.metadata === void 0 && selectedModel.ref === void 0 ? {} : {
|
|
5435
|
+
metadata: {
|
|
5436
|
+
...body.metadata ?? {},
|
|
5437
|
+
...selectedModel.ref === void 0 ? {} : { [STUDIO_MODEL_METADATA_KEY]: selectedModel.ref }
|
|
5438
|
+
}
|
|
5439
|
+
}
|
|
4981
5440
|
})
|
|
4982
5441
|
);
|
|
4983
5442
|
}
|
|
5443
|
+
if (session !== void 0 && selectedModel.ref !== void 0) {
|
|
5444
|
+
for (const warning of selectedModel.warnings) {
|
|
5445
|
+
await appendSessionLog(stores.sessions, {
|
|
5446
|
+
sessionId: session.id,
|
|
5447
|
+
runId,
|
|
5448
|
+
level: "warn",
|
|
5449
|
+
category: "model",
|
|
5450
|
+
event: "model.warning",
|
|
5451
|
+
message: typeof warning.message === "string" ? warning.message : "Model warning",
|
|
5452
|
+
metadata: warning
|
|
5453
|
+
});
|
|
5454
|
+
}
|
|
5455
|
+
if (sessionModelRef(session.metadata) !== selectedModel.ref) {
|
|
5456
|
+
await stores.sessions?.updateSessionMetadata?.(session.id, {
|
|
5457
|
+
...session.metadata ?? {},
|
|
5458
|
+
[STUDIO_MODEL_METADATA_KEY]: selectedModel.ref
|
|
5459
|
+
});
|
|
5460
|
+
}
|
|
5461
|
+
}
|
|
4984
5462
|
const memoryMetadata = {
|
|
4985
5463
|
agentId,
|
|
4986
5464
|
...body.metadata ?? {},
|
|
5465
|
+
...selectedModel.ref === void 0 ? {} : { [STUDIO_MODEL_METADATA_KEY]: selectedModel.ref },
|
|
4987
5466
|
studioRunId: runId
|
|
4988
5467
|
};
|
|
4989
5468
|
const promptMessage = normalizePromptMessage(body.message);
|
|
4990
5469
|
const sessionStore = stores.sessions;
|
|
4991
|
-
const shouldPersistSessionMessages = session !== void 0 && sessionStore !== void 0 && !usesStoreAsAgentMemory(
|
|
5470
|
+
const shouldPersistSessionMessages = session !== void 0 && sessionStore !== void 0 && !usesStoreAsAgentMemory(runAgent, sessionStore);
|
|
4992
5471
|
if (shouldPersistSessionMessages) {
|
|
4993
5472
|
await sessionStore.append({
|
|
4994
5473
|
context: { sessionId: session.id, metadata: memoryMetadata },
|
|
@@ -4997,7 +5476,7 @@ function createStudioApp(options) {
|
|
|
4997
5476
|
messages: [promptMessage]
|
|
4998
5477
|
});
|
|
4999
5478
|
}
|
|
5000
|
-
const request = session !== void 0 ?
|
|
5479
|
+
const request = session !== void 0 ? runAgent.memory === void 0 ? runAgent.prompt([...session.messages, promptMessage]) : runAgent.session(session.id, { metadata: memoryMetadata }).prompt(body.message) : runAgent.prompt(
|
|
5001
5480
|
body.history !== void 0 ? [...body.history, promptMessage] : body.message
|
|
5002
5481
|
);
|
|
5003
5482
|
if (body.maxTurns !== void 0) {
|
|
@@ -5015,13 +5494,13 @@ function createStudioApp(options) {
|
|
|
5015
5494
|
const runtimeEvents = new AsyncEventQueue();
|
|
5016
5495
|
const effectiveHook = composeHooks(
|
|
5017
5496
|
composeHooks(
|
|
5018
|
-
|
|
5497
|
+
runAgent.hook,
|
|
5019
5498
|
approvalRuntime.createHook({
|
|
5020
5499
|
runId,
|
|
5021
5500
|
agentId,
|
|
5022
5501
|
...session?.id === void 0 ? {} : { sessionId: session.id },
|
|
5023
5502
|
...body.metadata === void 0 ? {} : { metadata: body.metadata },
|
|
5024
|
-
getTool: (toolName) =>
|
|
5503
|
+
getTool: (toolName) => runAgent.getTool(toolName),
|
|
5025
5504
|
emit: (event) => runtimeEvents.push(event)
|
|
5026
5505
|
})
|
|
5027
5506
|
),
|
|
@@ -5060,13 +5539,13 @@ function createStudioApp(options) {
|
|
|
5060
5539
|
}
|
|
5061
5540
|
const effectiveHook = composeHooks(
|
|
5062
5541
|
composeHooks(
|
|
5063
|
-
|
|
5542
|
+
runAgent.hook,
|
|
5064
5543
|
approvalRuntime.createHook({
|
|
5065
5544
|
runId,
|
|
5066
5545
|
agentId,
|
|
5067
5546
|
...session?.id === void 0 ? {} : { sessionId: session.id },
|
|
5068
5547
|
...body.metadata === void 0 ? {} : { metadata: body.metadata },
|
|
5069
|
-
getTool: (toolName) =>
|
|
5548
|
+
getTool: (toolName) => runAgent.getTool(toolName)
|
|
5070
5549
|
})
|
|
5071
5550
|
),
|
|
5072
5551
|
questionRuntime.createHook({
|
|
@@ -5322,6 +5801,24 @@ var SqliteSessionStore = class {
|
|
|
5322
5801
|
const row = this.getSessionRow(id);
|
|
5323
5802
|
return row === void 0 ? void 0 : toSession(row, this.listSessionMessages(id), this.listSessionRunRows(id));
|
|
5324
5803
|
}
|
|
5804
|
+
updateSessionMetadata(id, metadata) {
|
|
5805
|
+
const db = this.database();
|
|
5806
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5807
|
+
const result = db.prepare(
|
|
5808
|
+
`UPDATE anvia_studio_sessions
|
|
5809
|
+
SET metadata_json = $metadata,
|
|
5810
|
+
updated_at = $now
|
|
5811
|
+
WHERE id = $id`
|
|
5812
|
+
).run({
|
|
5813
|
+
$id: id,
|
|
5814
|
+
$metadata: metadata === void 0 ? null : JSON.stringify(metadata),
|
|
5815
|
+
$now: now
|
|
5816
|
+
});
|
|
5817
|
+
if (result.changes === 0) {
|
|
5818
|
+
return void 0;
|
|
5819
|
+
}
|
|
5820
|
+
return this.getSession(id);
|
|
5821
|
+
}
|
|
5325
5822
|
load(context) {
|
|
5326
5823
|
const session = this.getSession(context.sessionId);
|
|
5327
5824
|
return Promise.resolve(session?.messages ?? []);
|