feat: 全新智能体功能

- 基于先进智能体框架,增加智能体编排功能
- 增加智能体聊天,并对接持久化
This commit is contained in:
2026-05-25 11:42:48 +08:00
parent 6c3d98eaac
commit 72df00f25b
168 changed files with 22045 additions and 400 deletions

View File

@@ -3,9 +3,11 @@
</template>
<script setup lang="ts">
import { Tinyflow as TinyflowNative, TinyflowOptions } from '@tinyflow-ai/ui';
import {Tinyflow as TinyflowNative, TinyflowOptions} from '@tinyflow-ai/ui';
import '@tinyflow-ai/ui/dist/index.css';
import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
import {nextTick, onMounted, onUnmounted, ref, useAttrs, watch} from 'vue';
type TinyflowDataOption = Exclude<TinyflowOptions['data'], string | undefined>;
const props = defineProps<
{
@@ -15,7 +17,25 @@ const props = defineProps<
>();
const divRef = ref<HTMLDivElement | null>(null);
const attrs = useAttrs();
let tinyflow: TinyflowNative | null = null;
let mountedDataReady = false;
let lastAppliedDataSignature = '';
function normalizeOptionKey(key: string) {
return key.replace(/-([a-z])/g, (_match: string, letter: string) =>
letter.toUpperCase(),
);
}
function normalizeOptions(source: Record<string, unknown>) {
return Object.fromEntries(
Object.entries(source).map(([key, value]) => [
normalizeOptionKey(key),
value,
]),
);
}
// 安全深拷贝工具函数
function safeDeepClone<T>(obj: T): T {
@@ -36,18 +56,45 @@ function safeDeepClone<T>(obj: T): T {
}
}
function createDataSignature(data: unknown) {
if (data == null || typeof data === 'string') {
return String(data ?? '');
}
try {
return JSON.stringify(data);
} catch {
return '';
}
}
function cloneDataIfChanged(data: TinyflowOptions['data']) {
if (data == null || typeof data === 'string') {
return null;
}
const signature = createDataSignature(data);
if (signature && signature === lastAppliedDataSignature) {
return null;
}
lastAppliedDataSignature = signature;
return safeDeepClone(data as TinyflowDataOption);
}
onMounted(() => {
if (divRef.value) {
// 净化 props.data避免响应式对象或函数污染
const cleanedProps = { ...props } as any;
const cleanedProps = {
...normalizeOptions(attrs),
...props,
} as any;
if ('data' in cleanedProps && cleanedProps.data != null) {
cleanedProps.data = safeDeepClone(cleanedProps.data);
cleanedProps.data = cloneDataIfChanged(cleanedProps.data);
}
tinyflow = new TinyflowNative({
...cleanedProps,
element: divRef.value,
});
mountedDataReady = true;
}
});
@@ -56,6 +103,8 @@ onUnmounted(() => {
tinyflow.destroy();
tinyflow = null;
}
mountedDataReady = false;
lastAppliedDataSignature = '';
});
watch(
@@ -67,6 +116,24 @@ watch(
},
);
watch(
() => props.data,
(data) => {
if (
tinyflow &&
mountedDataReady &&
data != null &&
typeof data !== 'string'
) {
const clonedData = cloneDataIfChanged(data);
if (clonedData) {
tinyflow.updateData(clonedData, { preserveViewport: true });
}
}
},
{ deep: true },
);
const getData = () => {
if (tinyflow) {
return tinyflow.getData();
@@ -103,10 +170,26 @@ const fitView = async (options?: { duration?: number; padding?: number }) => {
return false;
};
const updateData = (
data: TinyflowOptions['data'],
options?: { preserveViewport?: boolean },
) => {
if (tinyflow && data != null && typeof data !== 'string') {
const clonedData = cloneDataIfChanged(data);
if (!clonedData) {
return true;
}
return tinyflow.updateData(clonedData, options);
}
console.warn('Tinyflow instance is not initialized');
return false;
};
defineExpose({
getData,
getInstance,
focusNode,
fitView,
updateData,
});
</script>