Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions ui/src/api/admin/workspace/model/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ const putPauseModelDownload = (modelId: string) => {
return put<undefined, boolean>(`${getPrefix()}/${modelId}/pause_download`)
}



export default {
deleteModel,
getModelDetail,
Expand Down
43 changes: 11 additions & 32 deletions ui/src/components/business/model-select/index.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { computed, ref } from 'vue'
import { Check } from '@element-plus/icons-vue'
import ProviderApi from '@/api/admin/workspace/model/provider'
import { MODEL_STATUS } from '@/api/enums'
import type { ModelProviderItem, ModelItem } from '@/api/types'

import { groupBy } from 'lodash'
defineOptions({
name: 'ModelSelect',
inheritAttrs: false,
})

type ModelOptions = Record<string, ModelItem[]>

interface ModelOptionGroup {
icon: string
models: ModelItem[]
Expand All @@ -21,23 +18,24 @@ interface ModelOptionGroup {

const props = withDefaults(
defineProps<{
modelType?: string
modelValue: string
options: ModelOptions | null
options: ModelItem[]
providerOptions: ModelProviderItem[]
}>(),
{
modelType: '',
modelValue: '',
options: null,
options: () => [],
providerOptions: () => [],
},
)

const _options = computed(() => {
return groupBy(props.options, 'provider')
})
const emit = defineEmits<{
change: [modelId: string]
'update:modelValue': [modelId: string]
}>()

const providerOptions = ref<ModelProviderItem[]>([])
const loading = ref(false)

const selectedModelId = computed({
Expand All @@ -50,10 +48,10 @@ const selectedModelId = computed({

const modelOptionGroups = computed<ModelOptionGroup[]>(() => {
const providerMap = new Map(
providerOptions.value.map((provider) => [provider.provider, provider]),
props.providerOptions.map((provider) => [provider.provider, provider]),
)

return Object.entries(props.options ?? {}).map(([provider, models]) => {
return Object.entries(_options.value ?? {}).map(([provider, models]) => {
const providerOption = providerMap.get(provider)
return {
icon: providerOption?.icon ?? '',
Expand All @@ -74,25 +72,6 @@ const selectedProviderIcon = computed(
models.some(({ id }) => id === selectedModelId.value),
)?.icon ?? '',
)

function loadProviderOptions() {
loading.value = true

return ProviderApi.getProviderList()
.then((providers) => {
providerOptions.value = providers
})
.catch(() => {
providerOptions.value = []
})
.finally(() => {
loading.value = false
})
}

onMounted(() => {
loadProviderOptions()
})
</script>

<template>
Expand Down
4 changes: 4 additions & 0 deletions ui/src/views/workflow/ApplicationWorkflowView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ function handleSave() {
loading.value = false
})
}
const defaultWorkflow = {
nodes: [],
deges: [],
}

function loadApplicationDetail() {
loading.value = true
Expand Down
9 changes: 5 additions & 4 deletions ui/src/workflow-canvas/core/CustomLine.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
<script setup lang="ts">
import type { BaseEdgeModel } from '@logicflow/core'

const props = defineProps<{ model: BaseEdgeModel }>()
import { inject } from 'vue'
const getModel = inject('getModel') as () => BaseEdgeModel
const model = getModel()
const deleteEdge = () => {
props.model.graphModel.deleteEdgeById(props.model.id)
model.graphModel.deleteEdgeById(model.id)
}
</script>
<template>
<div class="custom-edge cursor" @mouseup.stop @click.stop v-show="props.model.isHovered">
<div class="custom-edge cursor" @mouseup.stop @click.stop v-show="model.isHovered">
<svg
@click="deleteEdge"
width="22"
Expand Down
30 changes: 7 additions & 23 deletions ui/src/workflow-canvas/core/edge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import {
type GraphModel,
} from '@logicflow/core'
import type { App } from 'vue'
import { createApp, h as vh } from 'vue'
import { isActive, connect, disconnect } from './teleport'
import { connect, disconnect } from './teleport'
import CustomLine from './CustomLine.vue'

function isMouseInElement(element: Element, event: PointerEvent) {
Expand Down Expand Up @@ -47,25 +46,12 @@ class CustomEdge2 extends BezierEdge {
protected renderVueComponent(root: HTMLDivElement) {
this.unmountVueComponent()
this.root = root
const { graphModel } = this.props
if (root) {
if (isActive()) {
connect(
this.targetId(),
CustomLine,
root,
this.props.model,
graphModel,
(node: BaseEdgeModel, graph: GraphModel) => {
return { model: node, graph }
},
)
} else {
this.customLineApp = createApp({
render: () => vh(CustomLine, { model: this.props.model }),
})
this.customLineApp?.mount(root)
}
connect(this.targetId(), CustomLine, root, () => {
return {
getModel: () => this.props.model,
}
})
}
}
protected targetId() {
Expand All @@ -78,9 +64,7 @@ class CustomEdge2 extends BezierEdge {
if (super.componentWillUnmount) {
super.componentWillUnmount()
}
if (isActive()) {
disconnect(this.targetId())
}
disconnect(this.targetId())
this.unmountVueComponent()
}
/**
Expand Down
6 changes: 3 additions & 3 deletions ui/src/workflow-canvas/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ const props = withDefaults(
const nodeModules = import.meta.glob<{ default: LogicFlow.RegisterConfig }>('./nodes/**/index.ts', {
eager: true,
})
const workflow_mode = inject('workflowMode') || WorkflowMode.Application
const loop_workflow_mode = inject('loopWorkflowMode') || WorkflowMode.ApplicationLoop
const workflow_mode = inject('workflowMode', WorkflowMode.Application)
const loop_workflow_mode = inject('loopWorkflowMode', WorkflowMode.ApplicationLoop)

const TeleportContainer = getTeleport()
const lf = shallowRef<LogicFlow>()
Expand Down Expand Up @@ -69,7 +69,6 @@ function renderGraphData(data: LogicFlow.GraphConfigData = props.data ?? {}) {
})

initDefaultShortcut(lf.value, lf.value.graphModel)
console.log(Object.values(nodeModules).map(({ default: node }) => node))
lf.value.batchRegister([...Object.values(nodeModules).map(({ default: node }) => node), AppEdge])
lf.value.setDefaultEdgeType('app-edge')
lf.value.render(data ? data : {})
Expand All @@ -82,6 +81,7 @@ function renderGraphData(data: LogicFlow.GraphConfigData = props.data ?? {}) {
getGraph: () => graph,
workflowMode: workflow_mode,
loopWorkflowMode: loop_workflow_mode,
apiType: 'workspace',
})
lf.value.graphModel.eventCenter.on('delete_edge', (edgeIds: string[]) => {
edgeIds.forEach((edgeId) => lf.value?.deleteEdge(edgeId))
Expand Down
35 changes: 29 additions & 6 deletions ui/src/workflow-canvas/nodes/ai-chat-node/index.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
<script setup lang="ts">
import { computed, inject, onMounted, useTemplateRef } from 'vue'

import { set } from 'lodash'
import { computed, inject, onMounted, ref, useTemplateRef } from 'vue'
import ModelSelect from '@/components/business/model-select/index.vue'
import { groupBy, set } from 'lodash'
import type { FormInstance } from 'element-plus'
import NodeCascader from '@/workflow-canvas/core/NodeCascader.vue'
import NodeContainer from '@/workflow-canvas/core/NodeContainer.vue'
import type { BaseNodeModel } from '@logicflow/core'
import { useWorkflowStore } from '@/workflow-canvas/store'
import type { ModelItem } from '@/api/types'

defineOptions({ name: 'WorkflowAiChatNode' })
const getModel = inject('getModel') as () => BaseNodeModel
const apiType = (inject('apiType') as string) || 'workspace'
const model = getModel()
interface AiChatNodeForm {
model_id: string
Expand Down Expand Up @@ -47,8 +50,22 @@ function validate() {
formRef.value?.validate(),
]).catch((error) => Promise.reject({ node: model, errMessage: error }))
}

onMounted(() => set(model, 'validate', validate))
const store = useWorkflowStore(apiType)
const modelList = ref<Array<ModelItem>>([])
const providerOptions = ref<Array<any>>([])
onMounted(() => {
set(model, 'validate', validate)
store
.getModelList({
model_type: 'LLM',
})
.then((data) => {
modelList.value = data
})
store.getProviderList().then((data) => {
providerOptions.value = data
})
})
</script>

<template>
Expand Down Expand Up @@ -88,7 +105,13 @@ onMounted(() => set(model, 'validate', validate))
class="w-full"
placeholder="请选择变量"
/>
<el-input v-else v-model="formData.model_id" placeholder="请输入 AI 模型 ID" />
<ModelSelect
v-else
placeholder="请输入 AI 模型 ID"
:options="modelList"
:provider-options="providerOptions"
v-model="formData.model_id"
></ModelSelect>
</el-form-item>

<el-form-item label="系统提示词">
Expand Down
1 change: 1 addition & 0 deletions ui/src/workflow-canvas/store/api/shared/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default {}
4 changes: 4 additions & 0 deletions ui/src/workflow-canvas/store/api/system/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import modelAPI from '@/api/admin/workspace/model/model'
export default {
getModelList: modelAPI.getModelList,
}
1 change: 1 addition & 0 deletions ui/src/workflow-canvas/store/api/workspace-shared/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default {}
6 changes: 6 additions & 0 deletions ui/src/workflow-canvas/store/api/workspace/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import modelAPI from '@/api/admin/workspace/model/model'
import providerAPI from '@/api/admin/workspace/model/provider'
export default {
getModelList: modelAPI.getModelList,
getProviderList: providerAPI.getProviderList,
}
64 changes: 64 additions & 0 deletions ui/src/workflow-canvas/store/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import type { ModelItem, ModelProviderItem, RequestParams } from '@/api/types'

type ApiModule = {
getModelList: (query?: RequestParams) => Promise<ModelItem[]>
getProviderList: () => Promise<ModelProviderItem[]>
}

const apiModules = import.meta.glob<{ default: ApiModule }>('./api/*/index.ts', {
eager: true,
})

const apiMap = Object.fromEntries(
Object.entries(apiModules).map(([path, mod]) => {
const key = path.match(/\.\/api\/(.+)\/index\.ts/)?.[1] ?? ''
return [key, mod.default]
}),
) as Record<string, ApiModule>

const cacheMap = new Map<string, Map<string, unknown>>()
const pendingMap = new Map<string, Map<string, Promise<unknown>>>()

export function useWorkflowStore(apiType: string): ApiModule {
const api = apiMap[apiType]
if (!api) {
throw new Error(`[useWorkflowStore] unknown apiType: "${apiType}"`)
}

const cache = cacheMap.get(apiType) ?? new Map<string, unknown>()
cacheMap.set(apiType, cache)

const pending = pendingMap.get(apiType) ?? new Map<string, Promise<unknown>>()
pendingMap.set(apiType, pending)

return {
getModelList(query?: RequestParams): Promise<ModelItem[]> {
const key = `model:${JSON.stringify(query ?? {})}`
if (cache.has(key)) return Promise.resolve(cache.get(key) as ModelItem[])
if (pending.has(key)) return pending.get(key) as Promise<ModelItem[]>

const promise = api.getModelList(query).then((list) => {
cache.set(key, list)
pending.delete(key)
return list
})

pending.set(key, promise)
return promise
},
getProviderList(): Promise<ModelProviderItem[]> {
const key = 'provider'
if (cache.has(key)) return Promise.resolve(cache.get(key) as ModelProviderItem[])
if (pending.has(key)) return pending.get(key) as Promise<ModelProviderItem[]>

const promise = api.getProviderList().then((list) => {
cache.set(key, list)
pending.delete(key)
return list
})

pending.set(key, promise)
return promise
},
}
}
Loading