227 lines
8.2 KiB
HTML
227 lines
8.2 KiB
HTML
<div class="llm-node"
|
|
[class.llm-node-error]="hasUpdateBlockError()"
|
|
[class.llm-node--human]="isHumanNode()">
|
|
@if (isCreatingOnServer()) {
|
|
<div class="llm-loading-overlay">
|
|
<div class="llm-loading-chip">
|
|
<span class="llm-spinner" aria-hidden="true"></span>
|
|
<span>Updating block...</span>
|
|
</div>
|
|
</div>
|
|
}
|
|
<div class="llm-header cursor-move">
|
|
<div class="llm-icon">
|
|
@if (isHumanNode()) {
|
|
<i class="bi bi-person-check-fill"></i>
|
|
} @else {
|
|
<img src="llm_node.png" alt="LLM" class="llm-icon-image" />
|
|
}
|
|
</div>
|
|
<div class="llm-header-content">
|
|
<span class="llm-title">{{ nodeTitle() }}</span>
|
|
<div class="llm-subtitle-row">
|
|
<span class="llm-subtitle">{{ name }}</span>
|
|
<button type="button" class="llm-name-edit-btn" title="Edit name" (pointerdown)="$event.stopPropagation()" (click)="openNameEditor($event)">
|
|
<i class="bi bi-pencil-square"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="llm-header-actions">
|
|
@if (hasUpdateBlockError()) {
|
|
<div class="llm-error-alert-wrap">
|
|
<div class="llm-error-alert">
|
|
<i class="bi bi-exclamation-circle-fill"></i>
|
|
</div>
|
|
<div class="llm-error-tooltip">
|
|
<div class="llm-error-title">Block update failed</div>
|
|
<div class="llm-error-item">{{ updateBlockErrorMessage() }}</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
@if (missingRequiredParams.length) {
|
|
<div class="llm-warning-wrap">
|
|
<div class="llm-warning">
|
|
<i class="bi bi-exclamation-triangle-fill"></i>
|
|
</div>
|
|
<div class="llm-warning-tooltip">
|
|
<div class="llm-warning-title">Missing required fields</div>
|
|
@for (missing of missingRequiredParams; track missing) {
|
|
<div class="llm-warning-item">{{ missing }}</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
}
|
|
<button type="button" class="llm-delete-btn" title="Delete node" (pointerdown)="$event.stopPropagation()" (click)="confirmDelete($event)">
|
|
<i class="bi bi-trash"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="llm-body">
|
|
<div class="llm-column">
|
|
<div class="llm-column-title">Inputs</div>
|
|
@for (input of inputs; track input.key) {
|
|
<div class="llm-row llm-row-input">
|
|
<div
|
|
refComponent
|
|
class="llm-socket"
|
|
[data]="{
|
|
type: 'socket',
|
|
side: 'input',
|
|
key: input.key,
|
|
nodeId: data.id,
|
|
payload: input.socket
|
|
}"
|
|
[emit]="emit">
|
|
</div>
|
|
<span class="llm-pill llm-pill-input">
|
|
{{ input.key }}
|
|
</span>
|
|
</div>
|
|
}
|
|
</div>
|
|
|
|
<div class="llm-column">
|
|
<div class="llm-column-title llm-column-title-right">Outputs</div>
|
|
@for (output of outputs; track output.key) {
|
|
<div class="llm-row llm-row-output">
|
|
<span class="llm-pill llm-pill-output">{{ output.key }}</span>
|
|
<div
|
|
refComponent
|
|
class="llm-socket llm-socket-right"
|
|
[data]="{
|
|
type: 'socket',
|
|
side: 'output',
|
|
key: output.key,
|
|
nodeId: data.id,
|
|
payload: output.socket
|
|
}"
|
|
[emit]="emit">
|
|
</div>
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="llm-params">
|
|
@if (parameterFieldGroups.length) {
|
|
<div class="llm-param-groups">
|
|
@for (group of parameterFieldGroups; track group.key) {
|
|
<fieldset class="llm-param-fieldset">
|
|
<legend class="llm-param-legend">{{ group.legend }}</legend>
|
|
<div class="llm-param-grid">
|
|
@for (field of group.fields; track field.path) {
|
|
<div class="llm-param-chip">
|
|
<div class="llm-param-row-head">
|
|
<span class="llm-param-key">{{ field.label }}</span>
|
|
<button
|
|
type="button"
|
|
class="llm-edit-btn"
|
|
[attr.title]="'Edit ' + field.label.toLowerCase()"
|
|
(pointerdown)="$event.stopPropagation()"
|
|
(click)="openParameterEditor(field.path, $event)">
|
|
<i class="bi bi-pen"></i>
|
|
</button>
|
|
</div>
|
|
<span class="llm-param-value">{{ field.value }}</span>
|
|
</div>
|
|
}
|
|
</div>
|
|
</fieldset>
|
|
}
|
|
</div>
|
|
}
|
|
|
|
@if (parameterFields.length) {
|
|
<div class="llm-param-grid">
|
|
@for (field of parameterFields; track field.path) {
|
|
<div class="llm-param-chip">
|
|
<div class="llm-param-row-head">
|
|
<span class="llm-param-key">{{ field.label }}</span>
|
|
<button
|
|
type="button"
|
|
class="llm-edit-btn"
|
|
[attr.title]="'Edit ' + field.label.toLowerCase()"
|
|
(pointerdown)="$event.stopPropagation()"
|
|
(click)="openParameterEditor(field.path, $event)">
|
|
<i class="bi bi-pen"></i>
|
|
</button>
|
|
</div>
|
|
<span class="llm-param-value">{{ field.value }}</span>
|
|
</div>
|
|
}
|
|
</div>
|
|
}
|
|
|
|
@if (hasMainContent()) {
|
|
<div class="llm-param-block">
|
|
<div class="llm-param-row-head">
|
|
<div class="llm-param-key">{{ mainContentLabel() }}</div>
|
|
<button type="button" class="llm-edit-btn" [attr.title]="'Edit ' + mainContentLabel().toLowerCase()" (pointerdown)="$event.stopPropagation()" (click)="openMainContentEditor($event)">
|
|
<i class="bi bi-pen"></i>
|
|
</button>
|
|
</div>
|
|
<div class="llm-param-text">
|
|
@if (!mainContentParts().length) {
|
|
-
|
|
} @else {
|
|
@for (part of mainContentParts(); track $index) {
|
|
@if (part.isDynamicInput) {
|
|
<span class="llm-inline-token">{{ formatDynamicInputToken(part.text) }}</span>
|
|
} @else {
|
|
<span>{{ part.text }}</span>
|
|
}
|
|
}
|
|
}
|
|
</div>
|
|
</div>
|
|
}
|
|
</div>
|
|
|
|
@if (localEditorOpen) {
|
|
<div class="llm-modal-backdrop" (pointerdown)="$event.stopPropagation()" (click)="closeSimpleParamEditor($event)">
|
|
<div class="llm-modal" (pointerdown)="$event.stopPropagation()" (click)="$event.stopPropagation()">
|
|
<div class="llm-modal-title">Edit {{ localEditorLabel }}</div>
|
|
<div class="llm-modal-field">
|
|
<label>{{ localEditorLabel }}</label>
|
|
@if (localEditorLoading) {
|
|
<input type="text" [ngModel]="localEditorValue" placeholder="Loading..." disabled (pointerdown)="$event.stopPropagation()" />
|
|
} @else if (localEditorType === 'boolean') {
|
|
<button
|
|
type="button"
|
|
class="node-toggle"
|
|
[class.node-toggle-on]="localEditorValue === 'true'"
|
|
[attr.aria-pressed]="localEditorValue === 'true'"
|
|
(pointerdown)="$event.stopPropagation()"
|
|
(click)="localEditorValue = localEditorValue === 'true' ? 'false' : 'true'">
|
|
<span class="node-toggle-track">
|
|
<span class="node-toggle-thumb"></span>
|
|
</span>
|
|
<span class="node-toggle-label">{{ localEditorValue === 'true' ? 'true' : 'false' }}</span>
|
|
</button>
|
|
} @else if (localEditorHasRetriever) {
|
|
<select [(ngModel)]="localEditorValue" (pointerdown)="$event.stopPropagation()">
|
|
<option value="">{{ localEditorOptions.length ? ('Select ' + (localEditorLabel | lowercase) + '...') : 'No options available' }}</option>
|
|
@for (option of localEditorOptions; track option) {
|
|
<option [value]="option">{{ option }}</option>
|
|
}
|
|
</select>
|
|
} @else {
|
|
<input
|
|
[type]="localEditorType === 'number' || localEditorType === 'integer' ? 'number' : 'text'"
|
|
[(ngModel)]="localEditorValue"
|
|
[attr.maxlength]="localEditorMaxLength"
|
|
[placeholder]="localEditorMaxLength ? 'Max ' + localEditorMaxLength + ' characters' : 'Value...'"
|
|
(pointerdown)="$event.stopPropagation()" />
|
|
}
|
|
</div>
|
|
<div class="llm-modal-actions">
|
|
<button type="button" class="llm-btn llm-btn-ghost" (pointerdown)="$event.stopPropagation()" (click)="closeSimpleParamEditor($event)">Cancel</button>
|
|
<button type="button" class="llm-btn llm-btn-primary" [disabled]="localEditorLoading" (pointerdown)="$event.stopPropagation()" (click)="saveSimpleParamEditor($event)">Save</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
</div>
|