feat: 全新智能体功能
- 基于先进智能体框架,增加智能体编排功能 - 增加智能体聊天,并对接持久化
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import type { useSvelteFlow } from '@xyflow/svelte';
|
||||
import { componentName } from './consts';
|
||||
import type { TinyflowData, TinyflowOptions, TinyflowTheme } from './types';
|
||||
import type {useSvelteFlow} from '@xyflow/svelte';
|
||||
import {componentName} from './consts';
|
||||
import {store} from './store/stores.svelte';
|
||||
import type {TinyflowData, TinyflowOptions, TinyflowTheme} from './types';
|
||||
|
||||
type FlowInstance = ReturnType<typeof useSvelteFlow>;
|
||||
|
||||
@@ -93,6 +94,37 @@ export class Tinyflow {
|
||||
return flow.toObject();
|
||||
}
|
||||
|
||||
updateData(data: TinyflowData, options?: { preserveViewport?: boolean }) {
|
||||
const flow = this._getFlowInstance();
|
||||
if (!flow) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const currentViewport = flow.getViewport();
|
||||
const currentNodes = flow.getNodes();
|
||||
const currentNodePositions = new Map(
|
||||
currentNodes.map((node) => [node.id, node.position]),
|
||||
);
|
||||
const nextNodes =
|
||||
options?.preserveViewport === true
|
||||
? (data.nodes || currentNodes).map((node) => {
|
||||
const currentPosition = currentNodePositions.get(node.id);
|
||||
return currentPosition
|
||||
? { ...node, position: { ...currentPosition } }
|
||||
: node;
|
||||
})
|
||||
: data.nodes || currentNodes;
|
||||
store.setNodes(nextNodes);
|
||||
store.setEdges(data.edges || flow.getEdges());
|
||||
|
||||
if (data.viewport && options?.preserveViewport !== true) {
|
||||
flow.setViewport(data.viewport, { duration: 0 });
|
||||
} else {
|
||||
flow.setViewport(currentViewport, { duration: 0 });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async focusNode(
|
||||
nodeId: string,
|
||||
options?: { duration?: number; zoom?: number },
|
||||
|
||||
@@ -65,6 +65,14 @@
|
||||
const readonly = options.readonly === true;
|
||||
let canvasLocked = $state(readonly);
|
||||
const hideBottomDock = options.hideBottomDock === true;
|
||||
const hideEdgePanel = options.hideEdgePanel === true;
|
||||
const hideMiniMap = options.hideMiniMap === true;
|
||||
const hideNodePicker = options.hideNodePicker === true;
|
||||
const nodesDraggable = options.nodesDraggable ?? !readonly;
|
||||
const nodesConnectable = options.nodesConnectable ?? !readonly;
|
||||
const elementsSelectable = options.elementsSelectable ?? !readonly;
|
||||
const dropEnabled = options.dropEnabled ?? !readonly;
|
||||
const connectionEnabled = nodesConnectable && !readonly;
|
||||
const availableNodes = getAvailableNodes(options);
|
||||
const onRunTest = options.onRunTest;
|
||||
|
||||
@@ -779,22 +787,22 @@
|
||||
bind:nodes={store.getNodes, store.setNodes}
|
||||
bind:edges={store.getEdges, store.setEdges}
|
||||
bind:viewport={store.getViewport, store.setViewport}
|
||||
nodesDraggable={!canvasLocked}
|
||||
nodesConnectable={!canvasLocked}
|
||||
elementsSelectable={!canvasLocked}
|
||||
nodesDraggable={nodesDraggable && !canvasLocked}
|
||||
nodesConnectable={nodesConnectable && !canvasLocked}
|
||||
elementsSelectable={elementsSelectable && !canvasLocked}
|
||||
panOnDrag={readonly ? true : !canvasLocked}
|
||||
zoomOnScroll={readonly ? true : !canvasLocked}
|
||||
zoomOnDoubleClick={readonly ? true : !canvasLocked}
|
||||
ondrop={readonly ? undefined : onDrop}
|
||||
ondragover={readonly ? undefined : onDragOver}
|
||||
ondrop={dropEnabled ? onDrop : undefined}
|
||||
ondragover={dropEnabled ? onDragOver : undefined}
|
||||
isValidConnection={isValidConnection}
|
||||
onconnectend={readonly ? undefined : onconnectend}
|
||||
onconnectstart={readonly ? undefined : onconnectstart}
|
||||
onconnect={readonly ? undefined : onconnect}
|
||||
onconnectend={connectionEnabled ? onconnectend : undefined}
|
||||
onconnectstart={connectionEnabled ? onconnectstart : undefined}
|
||||
onconnect={connectionEnabled ? onconnect : undefined}
|
||||
connectionRadius={50}
|
||||
connectionLineComponent={FlowConnectionLine}
|
||||
onedgeclick={(e) => {
|
||||
if (readonly) {
|
||||
if (readonly || hideEdgePanel) {
|
||||
return;
|
||||
}
|
||||
showEdgePanel = true;
|
||||
@@ -803,7 +811,7 @@
|
||||
onbeforeconnect={(edge: any) => normalizeEdgeBeforeConnect(edge)}
|
||||
ondelete={readonly ? undefined : onDelete}
|
||||
onclick={(e) => {
|
||||
if (readonly) {
|
||||
if (readonly || hideEdgePanel) {
|
||||
return;
|
||||
}
|
||||
const el = e.target as HTMLElement;
|
||||
@@ -825,7 +833,9 @@
|
||||
}}
|
||||
>
|
||||
<Background />
|
||||
<MiniMap />
|
||||
{#if !hideMiniMap}
|
||||
<MiniMap />
|
||||
{/if}
|
||||
|
||||
{#if showEdgePanel}
|
||||
<Panel>
|
||||
@@ -889,7 +899,7 @@
|
||||
</Panel>
|
||||
{/if}
|
||||
</SvelteFlow>
|
||||
{#if nodePickerVisible}
|
||||
{#if nodePickerVisible && !hideNodePicker}
|
||||
{#if pendingConnectionLine}
|
||||
<svg class="node-picker-connection-line" width="100%" height="100%">
|
||||
<FlowMarkerDefs id="tf-flow-inline-arrow-closed" />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import {EdgeLabel, type EdgeProps, getBezierPath} from '@xyflow/svelte';
|
||||
import FlowLinePath from './FlowLinePath.svelte';
|
||||
import {getOptions} from '../utils/NodeUtils';
|
||||
|
||||
let {
|
||||
sourceX,
|
||||
@@ -16,6 +17,15 @@
|
||||
labelStyle
|
||||
}: EdgeProps = $props();
|
||||
|
||||
const options = getOptions();
|
||||
const resolvedMarkerStart = $derived(options.hideEdgeMarkers === true ? undefined : markerStart);
|
||||
const resolvedMarkerEnd = $derived(options.hideEdgeMarkers === true ? undefined : markerEnd);
|
||||
const resolvedInteractionWidth = $derived(
|
||||
typeof options.edgeInteractionWidth === 'number'
|
||||
? options.edgeInteractionWidth
|
||||
: interactionWidth
|
||||
);
|
||||
const edgeAnimated = $derived(options.edgeAnimated === false ? false : true);
|
||||
const bezierPathResult = $derived.by(() =>
|
||||
getBezierPath({
|
||||
sourceX,
|
||||
@@ -32,13 +42,13 @@
|
||||
const labelY = $derived(bezierPathResult[2]);
|
||||
</script>
|
||||
|
||||
<FlowLinePath path={path} {markerStart} {markerEnd} animated={true} />
|
||||
<FlowLinePath path={path} markerStart={resolvedMarkerStart} markerEnd={resolvedMarkerEnd} animated={edgeAnimated} />
|
||||
|
||||
{#if interactionWidth > 0}
|
||||
{#if resolvedInteractionWidth > 0}
|
||||
<path
|
||||
d={path}
|
||||
stroke-opacity={0}
|
||||
stroke-width={interactionWidth}
|
||||
stroke-width={resolvedInteractionWidth}
|
||||
fill="none"
|
||||
class="svelte-flow__edge-interaction"
|
||||
></path>
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
useUpdateNodeInternals
|
||||
} from '@xyflow/svelte';
|
||||
import {Button, Collapse, FloatingTrigger, Input, Textarea} from '../base';
|
||||
import {type Snippet} from 'svelte';
|
||||
import {onDestroy, onMount} from 'svelte';
|
||||
import {onDestroy, onMount, type Snippet} from 'svelte';
|
||||
import {useDeleteNode} from '../utils/useDeleteNode.svelte';
|
||||
import {useCopyNode} from '../utils/useCopyNode.svelte';
|
||||
import {getOptions} from '../utils/NodeUtils';
|
||||
@@ -70,6 +69,13 @@
|
||||
const { copyNode } = useCopyNode();
|
||||
|
||||
const options = getOptions();
|
||||
const toolbarHidden = options.hideNodeToolbar === true;
|
||||
const nodeSettingHidden = options.hideNodeSetting === true;
|
||||
const handlesHidden = options.hideNodeHandles === true;
|
||||
const toolbarDeleteEnabled = $derived(allowDelete && !toolbarHidden);
|
||||
const toolbarCopyEnabled = $derived(allowCopy && !toolbarHidden);
|
||||
const toolbarExecuteEnabled = $derived(allowExecute && !toolbarHidden);
|
||||
const toolbarSettingEnabled = $derived(allowSetting && !toolbarHidden && !nodeSettingHidden);
|
||||
|
||||
const executeNode = () => {
|
||||
options.onNodeExecute?.(getNode(id)!);
|
||||
@@ -111,10 +117,10 @@
|
||||
</script>
|
||||
|
||||
|
||||
{#if allowExecute || allowCopy || allowDelete}
|
||||
{#if toolbarExecuteEnabled || toolbarCopyEnabled || toolbarDeleteEnabled || toolbarSettingEnabled}
|
||||
<NodeToolbar position={Position.Top} align="start">
|
||||
<div class="tf-node-toolbar">
|
||||
{#if allowDelete}
|
||||
{#if toolbarDeleteEnabled}
|
||||
<Button class="tf-node-toolbar-item" onclick={()=>{ deleteNode(id) }}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path
|
||||
@@ -122,7 +128,7 @@
|
||||
</svg>
|
||||
</Button>
|
||||
{/if}
|
||||
{#if allowCopy}
|
||||
{#if toolbarCopyEnabled}
|
||||
<Button class="tf-node-toolbar-item" onclick={()=>{copyNode(id)}}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path
|
||||
@@ -130,7 +136,7 @@
|
||||
</svg>
|
||||
</Button>
|
||||
{/if}
|
||||
{#if allowExecute}
|
||||
{#if toolbarExecuteEnabled}
|
||||
<Button class="tf-node-toolbar-item" onclick={executeNode}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path
|
||||
@@ -138,7 +144,7 @@
|
||||
</svg>
|
||||
</Button>
|
||||
{/if}
|
||||
{#if allowSetting}
|
||||
{#if toolbarSettingEnabled}
|
||||
<FloatingTrigger placement="bottom">
|
||||
<Button class="tf-node-toolbar-item">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
||||
@@ -294,10 +300,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if showTargetHandle}
|
||||
{#if showTargetHandle && !handlesHidden}
|
||||
<Handle type="target" position={Position.Left} style=" left: -12px;top: 20px" />
|
||||
{/if}
|
||||
{#if showSourceHandle}
|
||||
{#if showSourceHandle && !handlesHidden}
|
||||
<Handle type="source" position={Position.Right} style="right: -12px;top: 20px" />
|
||||
{/if}
|
||||
{@render handle?.()}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import NodeWrapper from '../core/NodeWrapper.svelte';
|
||||
import {type Node, type NodeProps, useNodesData, useSvelteFlow} from '@xyflow/svelte';
|
||||
import {type Node, useNodesData, useSvelteFlow} from '@xyflow/svelte';
|
||||
import {Button, Chosen, Heading, Input, Select, Textarea} from '../base';
|
||||
import RefParameterList from '../core/RefParameterList.svelte';
|
||||
import {getCurrentNodeId} from '#components/utils/NodeUtils';
|
||||
@@ -76,7 +76,7 @@
|
||||
let container = $state<HTMLElement | null>(null);
|
||||
$effect(() => {
|
||||
// 注意:由于 $effect 的 state 自动追踪问题,需要 data.expand 方在 if 里的最前面
|
||||
if (data.expand && container) {
|
||||
if ((data.expand || customNode.presentation === 'plain') && container) {
|
||||
container.append(externalElement);
|
||||
}
|
||||
});
|
||||
@@ -105,12 +105,14 @@
|
||||
|
||||
</script>
|
||||
|
||||
{#if customNode.presentation === 'plain'}
|
||||
<div bind:this={container} style={customNode.rootStyle||""} class={customNode.rootClass}></div>
|
||||
{:else}
|
||||
<NodeWrapper data={{...data, description: customNode.description}} {...getRestProps()}>
|
||||
|
||||
<NodeWrapper data={{...data, description: customNode.description}} {...getRestProps()}>
|
||||
|
||||
{#snippet icon()}
|
||||
{@html customNode.icon}
|
||||
{/snippet}
|
||||
{#snippet icon()}
|
||||
{@html customNode.icon}
|
||||
{/snippet}
|
||||
|
||||
{#if customNode.parametersEnable !== false}
|
||||
<div class="heading">
|
||||
@@ -251,7 +253,8 @@
|
||||
<OutputDefList />
|
||||
{/if}
|
||||
|
||||
</NodeWrapper>
|
||||
</NodeWrapper>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.heading {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Snippet } from 'svelte';
|
||||
import type { Node, useSvelteFlow } from '@xyflow/svelte';
|
||||
import type {Snippet} from 'svelte';
|
||||
import type {Node, useSvelteFlow} from '@xyflow/svelte';
|
||||
|
||||
export type TinyflowData = Partial<
|
||||
ReturnType<ReturnType<typeof useSvelteFlow>['toObject']>
|
||||
@@ -50,7 +50,9 @@ export type CustomNodeForm = {
|
||||
defaultValue?: string | number | boolean;
|
||||
attrs?: Record<string, any>;
|
||||
options?: SelectItem[];
|
||||
resolveValue?: (data: Record<string, any>) => string | number | boolean | undefined;
|
||||
resolveValue?: (
|
||||
data: Record<string, any>,
|
||||
) => string | number | boolean | undefined;
|
||||
resolveOptions?: (data: Record<string, any>) => SelectItem[];
|
||||
onValueChange?: (
|
||||
value: string | number | boolean | undefined,
|
||||
@@ -76,6 +78,7 @@ export type CustomNode = {
|
||||
icon?: string;
|
||||
sortNo?: number;
|
||||
group?: 'base' | 'tools';
|
||||
presentation?: 'default' | 'plain';
|
||||
renderFirst?: boolean;
|
||||
rootClass?: string;
|
||||
rootStyle?: string;
|
||||
@@ -100,6 +103,19 @@ export type TinyflowOptions = {
|
||||
data?: TinyflowData | string;
|
||||
readonly?: boolean;
|
||||
hideBottomDock?: boolean;
|
||||
hideEdgePanel?: boolean;
|
||||
hideMiniMap?: boolean;
|
||||
hideNodeHandles?: boolean;
|
||||
hideNodeToolbar?: boolean;
|
||||
hideNodePicker?: boolean;
|
||||
hideNodeSetting?: boolean;
|
||||
hideEdgeMarkers?: boolean;
|
||||
edgeAnimated?: boolean;
|
||||
edgeInteractionWidth?: number;
|
||||
nodesDraggable?: boolean;
|
||||
nodesConnectable?: boolean;
|
||||
elementsSelectable?: boolean;
|
||||
dropEnabled?: boolean;
|
||||
provider?: {
|
||||
llm?: () => SelectItem[] | Promise<SelectItem[]>;
|
||||
knowledge?: () => SelectItem[] | Promise<SelectItem[]>;
|
||||
|
||||
Reference in New Issue
Block a user