@c8y/ngx-components 1024.15.16 → 1024.16.10
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/fesm2022/c8y-ngx-components-ai-agent-chat.mjs +303 -63
- package/fesm2022/c8y-ngx-components-ai-agent-chat.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-ai-agents-html.mjs +9 -0
- package/fesm2022/c8y-ngx-components-ai-agents-html.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-ai-agents-scada.mjs +4 -0
- package/fesm2022/c8y-ngx-components-ai-agents-scada.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-ai-ai-chat.mjs +394 -52
- package/fesm2022/c8y-ngx-components-ai-ai-chat.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-ai.mjs +329 -19
- package/fesm2022/c8y-ngx-components-ai.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-bookmarks.mjs +1 -1
- package/fesm2022/c8y-ngx-components-bookmarks.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-context-dashboard.mjs +5 -1
- package/fesm2022/c8y-ngx-components-context-dashboard.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-datapoint-explorer-view.mjs +96 -77
- package/fesm2022/c8y-ngx-components-datapoint-explorer-view.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-datapoint-selector.mjs +92 -47
- package/fesm2022/c8y-ngx-components-datapoint-selector.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-device-profile.mjs +1 -1
- package/fesm2022/c8y-ngx-components-device-profile.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-echart.mjs +25 -4
- package/fesm2022/c8y-ngx-components-echart.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-ecosystem.mjs +1 -1
- package/fesm2022/c8y-ngx-components-ecosystem.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-protocol-opcua.mjs +1 -1
- package/fesm2022/c8y-ngx-components-protocol-opcua.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-register-device.mjs +5 -6
- package/fesm2022/c8y-ngx-components-register-device.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-repository-firmware.mjs +1 -1
- package/fesm2022/c8y-ngx-components-repository-firmware.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-repository-software.mjs +1 -1
- package/fesm2022/c8y-ngx-components-repository-software.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-sub-assets.mjs +30 -10
- package/fesm2022/c8y-ngx-components-sub-assets.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-time-context.mjs +13 -9
- package/fesm2022/c8y-ngx-components-time-context.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-trusted-certificates.mjs +1 -1
- package/fesm2022/c8y-ngx-components-trusted-certificates.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-definitions-html-widget-ai-config.mjs +3 -2
- package/fesm2022/c8y-ngx-components-widgets-definitions-html-widget-ai-config.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-implementations-asset-table.mjs +4 -6
- package/fesm2022/c8y-ngx-components-widgets-implementations-asset-table.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-implementations-scada.mjs +3 -2
- package/fesm2022/c8y-ngx-components-widgets-implementations-scada.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components.mjs +122 -24
- package/fesm2022/c8y-ngx-components.mjs.map +1 -1
- package/locales/de.po +147 -16
- package/locales/es.po +143 -11
- package/locales/fr.po +459 -324
- package/locales/ja_JP.po +143 -11
- package/locales/ko.po +143 -11
- package/locales/locales.pot +136 -10
- package/locales/nl.po +143 -11
- package/locales/pl.po +143 -11
- package/locales/pt_BR.po +143 -11
- package/locales/zh_CN.po +143 -11
- package/locales/zh_TW.po +143 -11
- package/package.json +1 -1
- package/types/c8y-ngx-components-ai-agent-chat.d.ts +111 -4
- package/types/c8y-ngx-components-ai-agent-chat.d.ts.map +1 -1
- package/types/c8y-ngx-components-ai-agents-html.d.ts.map +1 -1
- package/types/c8y-ngx-components-ai-agents-scada.d.ts.map +1 -1
- package/types/c8y-ngx-components-ai-ai-chat.d.ts +224 -10
- package/types/c8y-ngx-components-ai-ai-chat.d.ts.map +1 -1
- package/types/c8y-ngx-components-ai.d.ts +176 -6
- package/types/c8y-ngx-components-ai.d.ts.map +1 -1
- package/types/c8y-ngx-components-context-dashboard.d.ts.map +1 -1
- package/types/c8y-ngx-components-datapoint-explorer-view.d.ts +7 -2
- package/types/c8y-ngx-components-datapoint-explorer-view.d.ts.map +1 -1
- package/types/c8y-ngx-components-datapoint-selector.d.ts +63 -50
- package/types/c8y-ngx-components-datapoint-selector.d.ts.map +1 -1
- package/types/c8y-ngx-components-echart.d.ts +12 -0
- package/types/c8y-ngx-components-echart.d.ts.map +1 -1
- package/types/c8y-ngx-components-register-device.d.ts.map +1 -1
- package/types/c8y-ngx-components-sub-assets.d.ts +10 -1
- package/types/c8y-ngx-components-sub-assets.d.ts.map +1 -1
- package/types/c8y-ngx-components-time-context.d.ts +2 -0
- package/types/c8y-ngx-components-time-context.d.ts.map +1 -1
- package/types/c8y-ngx-components-widgets-implementations-scada.d.ts.map +1 -1
- package/types/c8y-ngx-components.d.ts +37 -5
- package/types/c8y-ngx-components.d.ts.map +1 -1
|
@@ -15,13 +15,13 @@ var DataStreamType;
|
|
|
15
15
|
DataStreamType["TOOL_CALL_STREAMING"] = "tool-call-streaming";
|
|
16
16
|
DataStreamType["TOOL_CALL_DELTA"] = "tool-call-delta";
|
|
17
17
|
DataStreamType["TOOL_RESULT"] = "tool-result";
|
|
18
|
+
DataStreamType["TOOL_APPROVAL_REQUEST"] = "tool-approval-request";
|
|
18
19
|
DataStreamType["REASONING"] = "reasoning";
|
|
19
20
|
DataStreamType["REASONING_DELTA"] = "reasoning-delta";
|
|
20
21
|
DataStreamType["REDACTED_REASONING"] = "redacted-reasoning";
|
|
21
22
|
DataStreamType["REASONING_SIGNATURE"] = "reasoning-signature";
|
|
22
23
|
DataStreamType["FINISH_REASONING"] = "finish-reasoning";
|
|
23
24
|
DataStreamType["FINISH"] = "finish";
|
|
24
|
-
DataStreamType["FINISH_STEP"] = "finish-step";
|
|
25
25
|
DataStreamType["ERROR"] = "error";
|
|
26
26
|
DataStreamType["DATA"] = "data";
|
|
27
27
|
DataStreamType["MESSAGE_ANNOTATIONS"] = "message-annotations";
|
|
@@ -57,7 +57,8 @@ class AIService {
|
|
|
57
57
|
headers: { 'Content-Type': 'application/json' }
|
|
58
58
|
});
|
|
59
59
|
if (!response.ok) {
|
|
60
|
-
|
|
60
|
+
const detail = await response.text().catch(() => '');
|
|
61
|
+
throw new Error(`Failed to create agent: ${response.statusText}${detail ? ` - ${detail}` : ''}`);
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
64
|
/**
|
|
@@ -294,32 +295,174 @@ class AIService {
|
|
|
294
295
|
// We will need to rewrite this to include tool outputs soon (effectively this means targetting LanguageModelV*ToolResultOutput).
|
|
295
296
|
// Once we have more fields than just role and content, we will need to convert between our format and
|
|
296
297
|
// the Vercel format expected by the agent manager (e.g. steps->content parts)
|
|
297
|
-
|
|
298
|
+
// The result of an approved tool call streams in with the NEXT assistant message after the
|
|
299
|
+
// resume, but on the wire it must sit next to its approval exchange (the agent strips
|
|
300
|
+
// approval parts from the model prompt, so an exchange without a result leaves a dangling
|
|
301
|
+
// tool call the model provider rejects). Index all results up front so
|
|
302
|
+
// convertAssistantMessageWithApprovals can pull them into the right message.
|
|
303
|
+
const resultsByToolCallId = new Map();
|
|
304
|
+
for (const m of messages) {
|
|
305
|
+
if (m.role !== 'assistant')
|
|
306
|
+
continue;
|
|
307
|
+
for (const part of m.content) {
|
|
308
|
+
if (part.type === 'tool-result') {
|
|
309
|
+
resultsByToolCallId.set(part.toolCallId, part);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
const serializedToolCallIds = new Set();
|
|
314
|
+
return messages.flatMap(m => {
|
|
298
315
|
if (m.role !== 'assistant') {
|
|
299
|
-
return
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
316
|
+
return [
|
|
317
|
+
{
|
|
318
|
+
role: m.role,
|
|
319
|
+
content: m.content
|
|
320
|
+
}
|
|
321
|
+
];
|
|
303
322
|
}
|
|
304
323
|
// For assistant messages, check if there's an object part
|
|
305
324
|
const objectPart = m.content.find((part) => part.type === 'object');
|
|
306
325
|
if (objectPart) {
|
|
307
326
|
// If there's an object part, parse and send the JSON object as content
|
|
308
|
-
return
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
327
|
+
return [
|
|
328
|
+
{
|
|
329
|
+
role: m.role,
|
|
330
|
+
content: objectPart.jsonContent
|
|
331
|
+
}
|
|
332
|
+
];
|
|
333
|
+
}
|
|
334
|
+
// Messages containing tool approvals must round-trip with full fidelity so the agent
|
|
335
|
+
// can match the user's decision to the pending tool call.
|
|
336
|
+
if (m.content.some(part => part.type === 'tool-approval-requested' || part.type === 'tool-approval-responded')) {
|
|
337
|
+
return this.convertAssistantMessageWithApprovals(m, resultsByToolCallId, serializedToolCallIds);
|
|
312
338
|
}
|
|
313
339
|
// Otherwise, send text parts as joined string
|
|
314
|
-
return
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
340
|
+
return [
|
|
341
|
+
{
|
|
342
|
+
role: m.role,
|
|
343
|
+
content: m.content
|
|
344
|
+
.filter((part) => part.type === 'text')
|
|
345
|
+
.map(part => part.text)
|
|
346
|
+
.join('\n\n')
|
|
347
|
+
}
|
|
348
|
+
];
|
|
321
349
|
});
|
|
322
350
|
}
|
|
351
|
+
/**
|
|
352
|
+
* Converts an assistant message containing tool approval parts into the wire format expected by
|
|
353
|
+
* the agent: an assistant message carrying `tool-call` and `tool-approval-request` parts, followed
|
|
354
|
+
* (once the user has decided) by a `tool` role message carrying the matching `tool-approval-response`
|
|
355
|
+
* parts.
|
|
356
|
+
*
|
|
357
|
+
* The agent strips approval parts from the prompt it sends to the model provider, so a *resolved*
|
|
358
|
+
* exchange must also carry the call's outcome in its tool message: the executed result (found via
|
|
359
|
+
* `resultsByToolCallId`, since it streams in with the following assistant message) or an
|
|
360
|
+
* `execution-denied` marker. Each such result is serialized exactly once - `serializedToolCallIds`
|
|
361
|
+
* tracks consumed calls so the copy stored in the following message is not emitted again as a
|
|
362
|
+
* duplicate tool call.
|
|
363
|
+
*/
|
|
364
|
+
convertAssistantMessageWithApprovals(m, resultsByToolCallId, serializedToolCallIds) {
|
|
365
|
+
const assistantContent = [];
|
|
366
|
+
const toolContent = [];
|
|
367
|
+
for (const part of m.content) {
|
|
368
|
+
switch (part.type) {
|
|
369
|
+
case 'text':
|
|
370
|
+
assistantContent.push({ type: 'text', text: part.text });
|
|
371
|
+
break;
|
|
372
|
+
case 'tool-approval-requested':
|
|
373
|
+
case 'tool-approval-responded':
|
|
374
|
+
if (!part.approval)
|
|
375
|
+
break;
|
|
376
|
+
assistantContent.push({
|
|
377
|
+
type: 'tool-call',
|
|
378
|
+
toolCallId: part.toolCallId,
|
|
379
|
+
toolName: part.toolName,
|
|
380
|
+
input: part.input ?? {}
|
|
381
|
+
});
|
|
382
|
+
assistantContent.push({
|
|
383
|
+
type: 'tool-approval-request',
|
|
384
|
+
approvalId: part.approval.approvalId,
|
|
385
|
+
toolCallId: part.toolCallId
|
|
386
|
+
});
|
|
387
|
+
if (part.approval.approved !== undefined) {
|
|
388
|
+
toolContent.push({
|
|
389
|
+
type: 'tool-approval-response',
|
|
390
|
+
approvalId: part.approval.approvalId,
|
|
391
|
+
approved: part.approval.approved,
|
|
392
|
+
...(part.approval.reason ? { reason: part.approval.reason } : {})
|
|
393
|
+
});
|
|
394
|
+
if (!serializedToolCallIds.has(part.toolCallId)) {
|
|
395
|
+
if (!part.approval.approved) {
|
|
396
|
+
toolContent.push({
|
|
397
|
+
type: 'tool-result',
|
|
398
|
+
toolCallId: part.toolCallId,
|
|
399
|
+
toolName: part.toolName,
|
|
400
|
+
output: {
|
|
401
|
+
type: 'execution-denied',
|
|
402
|
+
...(part.approval.reason ? { reason: part.approval.reason } : {})
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
serializedToolCallIds.add(part.toolCallId);
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
const result = resultsByToolCallId.get(part.toolCallId);
|
|
409
|
+
if (result) {
|
|
410
|
+
toolContent.push({
|
|
411
|
+
type: 'tool-result',
|
|
412
|
+
toolCallId: part.toolCallId,
|
|
413
|
+
toolName: part.toolName,
|
|
414
|
+
output: this.convertToolResultOutputForAgent(result)
|
|
415
|
+
});
|
|
416
|
+
serializedToolCallIds.add(part.toolCallId);
|
|
417
|
+
}
|
|
418
|
+
// No result yet: this approval is the one triggering the resume,
|
|
419
|
+
// and the agent executes the tool itself.
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
break;
|
|
424
|
+
case 'tool-result':
|
|
425
|
+
// Skip results already serialized next to their approval exchange in an earlier message
|
|
426
|
+
if (serializedToolCallIds.has(part.toolCallId))
|
|
427
|
+
break;
|
|
428
|
+
assistantContent.push({
|
|
429
|
+
type: 'tool-call',
|
|
430
|
+
toolCallId: part.toolCallId,
|
|
431
|
+
toolName: part.toolName,
|
|
432
|
+
input: part.input ?? {}
|
|
433
|
+
});
|
|
434
|
+
toolContent.push({
|
|
435
|
+
type: 'tool-result',
|
|
436
|
+
toolCallId: part.toolCallId,
|
|
437
|
+
toolName: part.toolName,
|
|
438
|
+
output: this.convertToolResultOutputForAgent(part)
|
|
439
|
+
});
|
|
440
|
+
break;
|
|
441
|
+
default:
|
|
442
|
+
// reasoning and step-start parts are not needed by the agent
|
|
443
|
+
break;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
const result = [{ role: 'assistant', content: assistantContent }];
|
|
447
|
+
if (toolContent.length > 0) {
|
|
448
|
+
result.push({ role: 'tool', content: toolContent });
|
|
449
|
+
}
|
|
450
|
+
return result;
|
|
451
|
+
}
|
|
452
|
+
/** Converts a tool output stored in our format back to the LanguageModelV*ToolResultOutput wire format. */
|
|
453
|
+
convertToolResultOutputForAgent(part) {
|
|
454
|
+
if (part.outputType === 'content') {
|
|
455
|
+
return { type: 'content', value: part.output };
|
|
456
|
+
}
|
|
457
|
+
if (part.error) {
|
|
458
|
+
return typeof part.output === 'string'
|
|
459
|
+
? { type: 'error-text', value: part.output }
|
|
460
|
+
: { type: 'error-json', value: part.output ?? null };
|
|
461
|
+
}
|
|
462
|
+
return typeof part.output === 'string'
|
|
463
|
+
? { type: 'text', value: part.output }
|
|
464
|
+
: { type: 'json', value: part.output ?? null };
|
|
465
|
+
}
|
|
323
466
|
/**
|
|
324
467
|
* Convert a tool output from the MCP/wire format (which is different/more compact than LanguageModelV3ToolResultOutput)
|
|
325
468
|
* into our own standard representation, which can be roundtripped to the format we need to provide when making future requests.
|
|
@@ -464,6 +607,14 @@ class AIService {
|
|
|
464
607
|
changedPart: this.addToolCallInfo('tool-result', data, message.content)
|
|
465
608
|
});
|
|
466
609
|
return;
|
|
610
|
+
case DataStreamType.TOOL_APPROVAL_REQUEST: {
|
|
611
|
+
// Emitted when a tool with `needsApproval` is called; the agent stops until the
|
|
612
|
+
// conversation is resent with the user's decision (see convertAssistantMessageWithApprovals).
|
|
613
|
+
const approvalPart = this.addToolCallInfo('tool-approval-requested', data.toolCall || data, message.content);
|
|
614
|
+
approvalPart.approval = { approvalId: data.approvalId };
|
|
615
|
+
observer.next({ message, changedPart: approvalPart });
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
467
618
|
case DataStreamType.FINISH:
|
|
468
619
|
message.finishReason = data.finishReason; // FUTURE could be anything, may not match the options in AIMessage
|
|
469
620
|
message.usage = data.totalUsage;
|
|
@@ -512,6 +663,19 @@ function defaultPruneMessagesForAgent(messages) {
|
|
|
512
663
|
// to avoid too many tokens and context window consumption.
|
|
513
664
|
// Some applications will want to entirely remove specific tool calls that have only transient use,
|
|
514
665
|
// which can be done by providing their own pruneMessagesForAgent function.
|
|
666
|
+
// Tool calls that went through an approval exchange must keep their results as well:
|
|
667
|
+
// the wire format pairs each resolved approval with its result, so pruning the result
|
|
668
|
+
// would leave the approved tool call dangling in the prompt sent to the model.
|
|
669
|
+
const approvalToolCallIds = new Set();
|
|
670
|
+
for (const m of messages) {
|
|
671
|
+
if (m.role !== 'assistant')
|
|
672
|
+
continue;
|
|
673
|
+
for (const part of m.content) {
|
|
674
|
+
if (part.type === 'tool-approval-requested' || part.type === 'tool-approval-responded') {
|
|
675
|
+
approvalToolCallIds.add(part.toolCallId);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
515
679
|
return messages.map(m => {
|
|
516
680
|
if (m.role !== 'assistant') {
|
|
517
681
|
return {
|
|
@@ -521,6 +685,11 @@ function defaultPruneMessagesForAgent(messages) {
|
|
|
521
685
|
}
|
|
522
686
|
const textParts = m.content.filter((part) => part.type === 'text');
|
|
523
687
|
const objectParts = m.content.filter((part) => part.type === 'object');
|
|
688
|
+
// Approval parts must survive pruning: the agent needs the pending tool call and the
|
|
689
|
+
// user's decision to resume a conversation that stopped for approval.
|
|
690
|
+
const approvalParts = m.content.filter((part) => part.type === 'tool-approval-requested' ||
|
|
691
|
+
part.type === 'tool-approval-responded' ||
|
|
692
|
+
(part.type === 'tool-result' && approvalToolCallIds.has(part.toolCallId)));
|
|
524
693
|
const content = [];
|
|
525
694
|
// Only add text part if there are text parts
|
|
526
695
|
if (textParts.length > 0) {
|
|
@@ -531,6 +700,7 @@ function defaultPruneMessagesForAgent(messages) {
|
|
|
531
700
|
}
|
|
532
701
|
// Preserve object parts for object agents
|
|
533
702
|
content.push(...objectParts);
|
|
703
|
+
content.push(...approvalParts);
|
|
534
704
|
return {
|
|
535
705
|
role: m.role,
|
|
536
706
|
content
|
|
@@ -538,9 +708,149 @@ function defaultPruneMessagesForAgent(messages) {
|
|
|
538
708
|
});
|
|
539
709
|
}
|
|
540
710
|
|
|
711
|
+
/**
|
|
712
|
+
* Remembers the user's "always approve" decisions for tool calls that require human approval.
|
|
713
|
+
*
|
|
714
|
+
* Decisions are held in memory only and are scoped to a chat session: each chat component
|
|
715
|
+
* instance identifies its conversation with a unique session ID, so approvals never outlive
|
|
716
|
+
* the conversation they were given in. Navigating away and back, reopening a dialog, or
|
|
717
|
+
* starting a new conversation creates a new session that prompts again. This applies both to
|
|
718
|
+
* single tools ("always approve this tool") and to the session-wide YOLO mode
|
|
719
|
+
* ({@link rememberAllTools}, "always approve all tools").
|
|
720
|
+
*
|
|
721
|
+
* Tools configured with `needsApproval` always prompt regardless of these preferences; that
|
|
722
|
+
* is enforced by the chat component, since it requires the agent definition. The chat
|
|
723
|
+
* component also offers a global "approve all tools" option persisted as a user preference,
|
|
724
|
+
* which is not managed by this service.
|
|
725
|
+
*
|
|
726
|
+
* Only approvals are remembered, never denials.
|
|
727
|
+
*/
|
|
728
|
+
class AiToolApprovalMemoryService {
|
|
729
|
+
constructor() {
|
|
730
|
+
/** Remembered approvals, keyed by chat session ID. */
|
|
731
|
+
this.sessions = new Map();
|
|
732
|
+
this.nextSessionId = 0;
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* Returns a new chat session ID, unique for the lifetime of the page. Chat components request
|
|
736
|
+
* one per conversation and pass it to the other methods to scope their approvals.
|
|
737
|
+
*/
|
|
738
|
+
newSessionId() {
|
|
739
|
+
return `chat-session-${++this.nextSessionId}`;
|
|
740
|
+
}
|
|
741
|
+
/** Remembers that calls of the given tool are always approved within the given chat session. */
|
|
742
|
+
rememberTool(sessionId, toolName) {
|
|
743
|
+
this.session(sessionId).tools.add(toolName);
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Enables YOLO mode for the given chat session: every tool of the conversation is approved
|
|
747
|
+
* automatically, without prompting, until the conversation ends.
|
|
748
|
+
*/
|
|
749
|
+
rememberAllTools(sessionId) {
|
|
750
|
+
this.session(sessionId).approveAll = true;
|
|
751
|
+
}
|
|
752
|
+
/** Whether the user chose to always approve the given tool within the given chat session. */
|
|
753
|
+
isToolRemembered(sessionId, toolName) {
|
|
754
|
+
return this.sessions.get(sessionId)?.tools.has(toolName) ?? false;
|
|
755
|
+
}
|
|
756
|
+
/** Whether YOLO mode is enabled for the given chat session (see {@link rememberAllTools}). */
|
|
757
|
+
isApproveAllToolsEnabled(sessionId) {
|
|
758
|
+
return this.sessions.get(sessionId)?.approveAll ?? false;
|
|
759
|
+
}
|
|
760
|
+
/** Forgets the remembered approvals of a chat session, e.g. when the conversation ends. */
|
|
761
|
+
endSession(sessionId) {
|
|
762
|
+
this.sessions.delete(sessionId);
|
|
763
|
+
}
|
|
764
|
+
/** Forgets all remembered approvals of all sessions. */
|
|
765
|
+
clear() {
|
|
766
|
+
this.sessions.clear();
|
|
767
|
+
}
|
|
768
|
+
session(sessionId) {
|
|
769
|
+
let session = this.sessions.get(sessionId);
|
|
770
|
+
if (!session) {
|
|
771
|
+
session = { approveAll: false, tools: new Set() };
|
|
772
|
+
this.sessions.set(sessionId, session);
|
|
773
|
+
}
|
|
774
|
+
return session;
|
|
775
|
+
}
|
|
776
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: AiToolApprovalMemoryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
777
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: AiToolApprovalMemoryService, providedIn: 'root' }); }
|
|
778
|
+
}
|
|
779
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: AiToolApprovalMemoryService, decorators: [{
|
|
780
|
+
type: Injectable,
|
|
781
|
+
args: [{ providedIn: 'root' }]
|
|
782
|
+
}] });
|
|
783
|
+
|
|
784
|
+
/** REST resource segments the `cumulocity-api-request` tool commonly queries, mapped to a readable plural noun. */
|
|
785
|
+
const RESOURCE_LABELS = {
|
|
786
|
+
managedObjects: gettext('inventory objects'),
|
|
787
|
+
alarms: gettext('alarms'),
|
|
788
|
+
measurements: gettext('measurements'),
|
|
789
|
+
events: gettext('events'),
|
|
790
|
+
operations: gettext('operations'),
|
|
791
|
+
users: gettext('users'),
|
|
792
|
+
applications: gettext('applications'),
|
|
793
|
+
identity: gettext('external IDs')
|
|
794
|
+
};
|
|
795
|
+
/** Common `fragmentType`/`type` filter values used to narrow an inventory query, mapped to a more specific noun than the generic "inventory objects" label. */
|
|
796
|
+
const FRAGMENT_TYPE_LABELS = {
|
|
797
|
+
c8y_IsDevice: gettext('devices'),
|
|
798
|
+
c8y_IsDeviceGroup: gettext('device groups'),
|
|
799
|
+
c8y_IsAsset: gettext('assets')
|
|
800
|
+
};
|
|
801
|
+
/**
|
|
802
|
+
* A `labelProvider` (see `ToolCallConfig`) for the `cumulocity-api-request` tool: the generic
|
|
803
|
+
* Cumulocity REST query tool offered to AI agents to look up API data. For a tool call awaiting
|
|
804
|
+
* approval, produces a human-readable summary of the query - e.g. "Look up: alarms with status:
|
|
805
|
+
* active" -
|
|
806
|
+
* instead of the raw `{ "query": "/alarm/alarms?status=ACTIVE" }`, so the user can judge the
|
|
807
|
+
* request without reading the API path themselves.
|
|
808
|
+
*
|
|
809
|
+
* Only applies to the `tool-approval-requested`/`tool-approval-responded` states; for every other
|
|
810
|
+
* state this returns `undefined` so the caller's own `executingLabel`/`completedLabel` is used
|
|
811
|
+
* instead. It also returns `undefined` for any query it doesn't recognize, which keeps the generic
|
|
812
|
+
* "Tool cumulocity-api-request requires approval" text and the auto-expanded raw input as a
|
|
813
|
+
* fallback - recognizes the common read endpoints (inventory, alarms, measurements, events,
|
|
814
|
+
* operations, users, applications, identity).
|
|
815
|
+
*
|
|
816
|
+
* Assumes the tool's `query` input is a REST path with no separate `method`/body fields, matching
|
|
817
|
+
* its documented purpose of verifying/looking up API data.
|
|
818
|
+
*/
|
|
819
|
+
function cumulocityApiRequestLabelProvider(tool, translateService) {
|
|
820
|
+
if (tool.type !== 'tool-approval-requested' && tool.type !== 'tool-approval-responded') {
|
|
821
|
+
return undefined;
|
|
822
|
+
}
|
|
823
|
+
const query = tool.input?.query;
|
|
824
|
+
const summary = summarizeQuery(query, translateService);
|
|
825
|
+
if (!summary)
|
|
826
|
+
return undefined;
|
|
827
|
+
if (tool.type === 'tool-approval-responded') {
|
|
828
|
+
return translateService.instant(tool.approval?.approved ? gettext('Approved: {{summary}}') : gettext('Denied: {{summary}}'), { summary });
|
|
829
|
+
}
|
|
830
|
+
return summary;
|
|
831
|
+
}
|
|
832
|
+
function summarizeQuery(query, translateService) {
|
|
833
|
+
if (!query)
|
|
834
|
+
return undefined;
|
|
835
|
+
const [path, search] = query.split('?');
|
|
836
|
+
const resourceKey = path.split('/').find(segment => RESOURCE_LABELS[segment]);
|
|
837
|
+
if (!resourceKey)
|
|
838
|
+
return undefined;
|
|
839
|
+
const params = new URLSearchParams(search || '');
|
|
840
|
+
const fragmentType = params.get('fragmentType') || params.get('type');
|
|
841
|
+
const resource = translateService.instant((fragmentType && FRAGMENT_TYPE_LABELS[fragmentType]) || RESOURCE_LABELS[resourceKey]);
|
|
842
|
+
const status = params.get('status');
|
|
843
|
+
return status
|
|
844
|
+
? translateService.instant(gettext('Look up: {{resource}} with status: {{status}}'), {
|
|
845
|
+
status: status.toLowerCase(),
|
|
846
|
+
resource
|
|
847
|
+
})
|
|
848
|
+
: translateService.instant(gettext('Look up: {{resource}}'), { resource });
|
|
849
|
+
}
|
|
850
|
+
|
|
541
851
|
/**
|
|
542
852
|
* Generated bundle index. Do not edit.
|
|
543
853
|
*/
|
|
544
854
|
|
|
545
|
-
export { AIService, DataStreamType, defaultPruneMessagesForAgent };
|
|
855
|
+
export { AIService, AiToolApprovalMemoryService, DataStreamType, cumulocityApiRequestLabelProvider, defaultPruneMessagesForAgent };
|
|
546
856
|
//# sourceMappingURL=c8y-ngx-components-ai.mjs.map
|