-
+
+ ) : undefined
+ }
+ icon={logo ? '' :
}
+ >
+ {' '}
+
{title}
diff --git a/frontend/packages/core/src/pages/playground/components/types.ts b/frontend/packages/core/src/pages/playground/components/types.ts
index 73eff4fc..e360509d 100644
--- a/frontend/packages/core/src/pages/playground/components/types.ts
+++ b/frontend/packages/core/src/pages/playground/components/types.ts
@@ -1,9 +1,20 @@
-export type ModelCardStatus = 'success' | 'failure'
+export type ModelStatus = 'enable' | 'abnormal'|'disable'
+export type KeyStatus ='normal' | 'abnormal'|'disable'
+
+export interface KeyData {
+ id: string
+ name: string
+ status: KeyStatus,
+}
export interface ModelData {
id: string
- type: string
- title: string
- status: ModelCardStatus
- defaultModel: string
+ name: string
+ logo: string
+ default_llm: string
+ status: ModelStatus
+ api_count: number
+ key_count: number
+ keys: KeyData[]
+ priority?: number
}
diff --git a/frontend/packages/core/src/pages/playground/index.tsx b/frontend/packages/core/src/pages/playground/index.tsx
index 53dd27b2..1561ca59 100644
--- a/frontend/packages/core/src/pages/playground/index.tsx
+++ b/frontend/packages/core/src/pages/playground/index.tsx
@@ -2,20 +2,23 @@
import { addEdge, NodeTypes, ReactFlow, useEdgesState, useNodesState } from '@xyflow/react'
import '@xyflow/react/dist/style.css'
-import { useCallback } from 'react'
+import { useCallback, useEffect, useState } from 'react'
import { KeyStatusNode, ModelCardNode, ServiceCardNode } from './components/NodeComponents'
import { ModelData } from './components/types'
import { LAYOUT } from './constants'
import './styles.css'
-const modelData: ModelData[] = [
- { id: 'openai', type: 'openai', title: 'OpenAI', status: 'success', defaultModel: 'gpt-4' },
- { id: 'anthropic', type: 'anthropic', title: 'Anthropic', status: 'success', defaultModel: 'claude-2' },
- { id: 'gemini', type: 'gemini', title: 'Google Gemini', status: 'failure', defaultModel: 'gemini-pro' },
- { id: 'mistral', type: 'mistral', title: 'Mistral AI', status: 'success', defaultModel: 'mistral-medium' },
- { id: 'cohere', type: 'cohere', title: 'Cohere', status: 'success', defaultModel: 'command' },
- { id: 'azure', type: 'azure', title: 'Azure OpenAI', status: 'success', defaultModel: 'gpt-4-turbo' }
-]
+interface ApiResponse {
+ data: {
+ backup: {
+ id: string
+ name: string
+ }
+ providers: ModelData[]
+ }
+ code: number
+ success: string
+}
const calculateNodePositions = (models: ModelData[], startY = LAYOUT.NODE_START_Y, gap = LAYOUT.NODE_GAP) => {
return models.reduce(
@@ -40,209 +43,339 @@ const nodeTypes: NodeTypes = {
serviceCard: ServiceCardNode
} as const
-const initialNodes = [
- {
- id: 'service',
- type: 'serviceCard',
- position: { x: LAYOUT.SERVICE_NODE_X, y: LAYOUT.NODE_START_Y },
- data: {}
- },
- ...modelData.map((model) => ({
- id: model.id,
- type: 'modelCard',
- position: calculateNodePositions(modelData)[model.id],
- data: {
- title: model.title,
- status: model.status,
- defaultModel: model.defaultModel
- }
- })),
- {
- id: 'openai-keys',
- type: 'keyCard',
- position: calculateNodePositions(modelData)['openai-keys'],
- data: {
- title: 'API Keys',
- keys: [
- { keyID: 1, status: 'success', priority: 1 },
- { keyID: 2, status: 'success', priority: 2 },
- { keyID: 3, status: 'failure', priority: 3 },
- { keyID: 4, status: 'success', priority: 4 },
- { keyID: 5, status: 'success', priority: 5 },
- { keyID: 6, status: 'failure', priority: 6 },
- { keyID: 7, status: 'success', priority: 7 },
- { keyID: 8, status: 'success', priority: 8 },
- { keyID: 9, status: 'failure', priority: 9 },
- { keyID: 10, status: 'success', priority: 10 },
- { keyID: 11, status: 'success', priority: 11 },
- { keyID: 12, status: 'failure', priority: 12 },
- { keyID: 13, status: 'success', priority: 13 },
- { keyID: 14, status: 'success', priority: 14 },
- { keyID: 15, status: 'failure', priority: 15 },
- { keyID: 16, status: 'success', priority: 16 },
- { keyID: 17, status: 'success', priority: 17 },
- { keyID: 18, status: 'failure', priority: 18 },
- { keyID: 19, status: 'success', priority: 19 },
- { keyID: 20, status: 'success', priority: 20 }
- ]
- }
- },
- {
- id: 'anthropic-keys',
- type: 'keyCard',
- position: calculateNodePositions(modelData)['anthropic-keys'],
- data: {
- title: 'API Keys',
- keys: [
- { keyID: 1, status: 'success', priority: 1 },
- { keyID: 2, status: 'success', priority: 2 }
- ]
- }
- },
- {
- id: 'gemini-keys',
- type: 'keyCard',
- position: calculateNodePositions(modelData)['gemini-keys'],
- data: {
- title: 'API Keys',
- keys: [
- { keyID: 1, status: 'failure', priority: 1 },
- { keyID: 2, status: 'failure', priority: 2 },
- { keyID: 3, status: 'failure', priority: 3 },
- { keyID: 4, status: 'failure', priority: 4 }
- ]
- }
- },
- {
- id: 'mistral-keys',
- type: 'keyCard',
- position: calculateNodePositions(modelData)['mistral-keys'],
- data: {
- title: 'API Keys',
- keys: []
- }
- },
- {
- id: 'cohere-keys',
- type: 'keyCard',
- position: calculateNodePositions(modelData)['cohere-keys'],
- data: {
- title: 'API Keys',
- keys: [
- { keyID: 1, status: 'failure', priority: 1 },
- { keyID: 2, status: 'success', priority: 2 },
- { keyID: 3, status: 'success', priority: 3 }
- ]
- }
- },
- {
- id: 'azure-keys',
- type: 'keyCard',
- position: calculateNodePositions(modelData)['azure-keys'],
- data: {
- title: 'API Keys',
- keys: [
- { keyID: 1, status: 'success', priority: 1 },
- { keyID: 2, status: 'success', priority: 2 },
- { keyID: 3, status: 'success', priority: 3 }
- ]
- }
- }
-]
-
-const initialEdges = [
- {
- id: 'service-openai',
- source: 'service',
- target: 'openai',
- label: 'apis(12)',
- style: { stroke: '#ddd', cursor: 'pointer' },
- data: {
- endLabel: 'apis(12)'
- // endLabelStyle: { fill: '#3d46f2', fontSize: 12, cursor: 'pointer' },
- // endLabelBgStyle: { fill: 'white', fillOpacity: 0.8 },
- },
- type: 'smoothstep',
- markerEnd: { type: 'arrow' }
- },
- {
- id: 'service-anthropic',
- source: 'service',
- target: 'anthropic',
- label: 'apis(8)',
- style: { stroke: '#ddd', cursor: 'pointer' },
- labelStyle: { fill: '#3d46f2', fontSize: 12, cursor: 'pointer' },
- labelBgStyle: { fill: 'white', fillOpacity: 0.8 },
- labelBgPadding: [4, 2],
- labelShowBg: true,
- type: 'smoothstep',
- markerEnd: { type: 'arrow' }
- },
- {
- id: 'service-gemini',
- source: 'service',
- target: 'gemini',
- label: 'apis(5)',
- style: { stroke: '#ddd', cursor: 'pointer' },
- labelStyle: { fill: '#3d46f2', fontSize: 12, cursor: 'pointer' },
- labelBgStyle: { fill: 'white', fillOpacity: 0.8 },
- labelBgPadding: [4, 2],
- labelShowBg: true,
- type: 'smoothstep',
- markerEnd: { type: 'arrow' }
- },
- {
- id: 'service-mistral',
- source: 'service',
- target: 'mistral',
- label: 'apis(4)',
- style: { stroke: '#ddd', cursor: 'pointer' },
- labelStyle: { fill: '#3d46f2', fontSize: 12, cursor: 'pointer' },
- labelBgStyle: { fill: 'white', fillOpacity: 0.8 },
- labelBgPadding: [4, 2],
- labelShowBg: true,
- type: 'smoothstep',
- markerEnd: { type: 'arrow' }
- },
- {
- id: 'service-cohere',
- source: 'service',
- target: 'cohere',
- label: 'apis(6)',
- style: { stroke: '#ddd', cursor: 'pointer' },
- labelStyle: { fill: '#3d46f2', fontSize: 12, cursor: 'pointer' },
- labelBgStyle: { fill: 'white', fillOpacity: 0.8 },
- labelBgPadding: [4, 2],
- labelShowBg: true,
- type: 'smoothstep',
- markerEnd: { type: 'arrow' }
- },
- {
- id: 'service-azure',
- source: 'service',
- target: 'azure',
- label: 'apis(10)',
- style: { stroke: '#ddd', cursor: 'pointer' },
- labelStyle: { fill: '#3d46f2', fontSize: 12, cursor: 'pointer' },
- labelBgStyle: { fill: 'white', fillOpacity: 0.8 },
- labelBgPadding: [4, 2],
- labelShowBg: true,
- type: 'smoothstep',
- markerEnd: { type: 'arrow' }
- },
- ...modelData.map((model) => ({
- id: `${model.id}-keys`,
- source: model.id,
- type: 'smoothstep',
- target: `${model.id}-keys`,
- animated: true,
- style: { stroke: '#ddd' }
- }))
-]
-
const Playground = () => {
- const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes)
- const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges)
+ const [modelData, setModelData] = useState
([])
+ const [nodes, setNodes, onNodesChange] = useNodesState([])
+ const [edges, setEdges, onEdgesChange] = useEdgesState([])
+
+ useEffect(() => {
+ // Mock API call - replace with actual API call
+ const mockApiResponse: ApiResponse = {
+ code: 0,
+ msg: 'default_value',
+ data: {
+ providers: [
+ {
+ id: '320000199612026216',
+ name: 'iFLYTEK SPARK',
+ logo: '\n\n\n',
+ status: 'enable',
+ default_llm: 'fakegpt-1.0',
+ api_count: 15,
+ key_count: 4,
+ keys: [
+ {
+ id: '140000200804035469',
+ name: 'Margaret Perez',
+ status: 'normal'
+ },
+ {
+ id: '460000198105142251',
+ name: 'Paul Moore',
+ status: 'disable'
+ },
+ {
+ id: '640000198609159464',
+ name: 'Timothy Clark',
+ status: 'disable'
+ },
+ {
+ id: '370000199901139021',
+ name: 'Mark Williams',
+ status: 'abnormal'
+ }
+ ]
+ },
+ {
+ id: '640000200408193359',
+ name: 'OpenRouter',
+ logo: '\n',
+ status: 'disable',
+ default_llm: 'fakegpt-1.0',
+ api_count: 2,
+ key_count: 7,
+ keys: [
+ {
+ id: '540000200004175839',
+ name: 'Maria Lewis',
+ status: 'abnormal'
+ },
+ {
+ id: '510000199111048228',
+ name: 'Melissa Wilson',
+ status: 'disable'
+ }
+ ]
+ },
+ {
+ id: '43000019760318266X',
+ name: '360 AI',
+ logo: '\n\n\n',
+ status: 'disable',
+ default_llm: 'fakegpt-1.0',
+ api_count: 3,
+ key_count: 5,
+ keys: [
+ {
+ id: '150000201012143900',
+ name: 'Donald Johnson',
+ status: 'abnormal'
+ },
+ {
+ id: '520000197104044707',
+ name: 'Daniel Perez',
+ status: 'normal'
+ },
+ {
+ id: '230000200610198413',
+ name: 'Kimberly Lee',
+ status: 'abnormal'
+ }
+ ]
+ },
+ {
+ id: '440000202405123330',
+ name: 'SiliconFlow',
+ logo: '\n',
+ status: 'abnormal',
+ default_llm: 'gpt-3.5-turbo-0125',
+ api_count: 6,
+ key_count: 6,
+ keys: [
+ {
+ id: '500000199805052887',
+ name: 'Sharon Robinson',
+ status: 'abnormal'
+ },
+ {
+ id: '110000198212026854',
+ name: 'Carol Thompson',
+ status: 'abnormal'
+ },
+ {
+ id: '340000201911295145',
+ name: 'Mark Davis',
+ status: 'disable'
+ },
+ {
+ id: '320000198302065610',
+ name: 'Donna Garcia',
+ status: 'disable'
+ }
+ ]
+ },
+ {
+ id: '620000199406027251',
+ name: 'FakeGPT',
+ logo: '\n\n\n',
+ status: 'disable',
+ default_llm: 'gpt-3.5-turbo-0125',
+ api_count: 15,
+ key_count: 2,
+ keys: [
+ {
+ id: '650000200812274267',
+ name: 'Jeffrey Taylor',
+ status: 'normal'
+ },
+ {
+ id: '420000197301023899',
+ name: 'Ruth Jones',
+ status: 'abnormal'
+ },
+ {
+ id: '820000201312084850',
+ name: 'Steven White',
+ status: 'disable'
+ }
+ ]
+ },
+ {
+ id: '520000200005228695',
+ name: '360 AI',
+ logo: '\n\n\n',
+ status: 'enable',
+ default_llm: 'fakegpt-1.0',
+ api_count: 19,
+ key_count: 9,
+ keys: [
+ {
+ id: '330000197006296104',
+ name: 'Karen Martinez',
+ status: 'abnormal'
+ },
+ {
+ id: '420000199601313686',
+ name: 'Scott Davis',
+ status: 'disable'
+ }
+ ]
+ },
+ {
+ id: '500000202103204115',
+ name: 'Google Gemini',
+ logo: '\n\n\n',
+ status: 'abnormal',
+ default_llm: 'gpt-3.5-turbo-0125',
+ api_count: 2,
+ key_count: 7,
+ keys: [
+ {
+ id: '140000202101093813',
+ name: 'Jose Harris',
+ status: 'normal'
+ },
+ {
+ id: '410000201802115211',
+ name: 'Richard Johnson',
+ status: 'abnormal'
+ }
+ ]
+ },
+ {
+ id: '130000202402241558',
+ name: 'Google Gemini',
+ logo: '\n\n\n',
+ status: 'enable',
+ default_llm: 'gpt-3.5-turbo-0125',
+ api_count: 2,
+ key_count: 9,
+ keys: [
+ {
+ id: '370000198910185957',
+ name: 'Christopher Thomas',
+ status: 'abnormal'
+ },
+ {
+ id: '450000201505255688',
+ name: 'Mark Williams',
+ status: 'normal'
+ },
+ {
+ id: '150000198812074566',
+ name: 'David Young',
+ status: 'disable'
+ }
+ ]
+ },
+ {
+ id: '120000200209263834',
+ name: 'AWS Bedrock',
+ logo: '\n',
+ status: 'enable',
+ default_llm: 'fakegpt-1.0',
+ api_count: 6,
+ key_count: 1,
+ keys: [
+ {
+ id: '310000200804263420',
+ name: 'Daniel Lewis',
+ status: 'normal'
+ },
+ {
+ id: '370000201306032780',
+ name: 'Richard Walker',
+ status: 'abnormal'
+ },
+ {
+ id: '500000200211133087',
+ name: 'Frank Jones',
+ status: 'disable'
+ },
+ {
+ id: '350000199105019471',
+ name: 'Carol Wilson',
+ status: 'abnormal'
+ }
+ ]
+ },
+ {
+ id: '110000198411203825',
+ name: 'deepseek',
+ logo: '\n',
+ status: 'abnormal',
+ default_llm: 'fakegpt-1.0',
+ api_count: 1,
+ key_count: 10,
+ keys: [
+ {
+ id: '210000200509114330',
+ name: 'Jason Martin',
+ status: 'abnormal'
+ }
+ ]
+ }
+ ],
+ backup: ''
+ },
+ msg_zh: 'default_value'
+ }
+ setModelData(mockApiResponse.data.providers)
+ }, [])
+
+ useEffect(() => {
+ if (!modelData.length) return
+
+ const newNodes = [
+ {
+ id: 'service',
+ type: 'serviceCard',
+ position: { x: LAYOUT.SERVICE_NODE_X, y: LAYOUT.NODE_START_Y },
+ data: {}
+ },
+ ...modelData.map((model) => ({
+ id: model.id,
+ type: 'modelCard',
+ position: calculateNodePositions(modelData)[model.id],
+ data: {
+ title: model.name,
+ status: model.status,
+ defaultModel: model.default_llm,
+ logo: model.logo
+ }
+ })),
+ ...modelData.map((model) => ({
+ id: `${model.id}-keys`,
+ type: 'keyCard',
+ position: calculateNodePositions(modelData)[`${model.id}-keys`],
+ data: {
+ title: 'API Keys',
+ keys: model.keys.map((key, index) => ({
+ keyID: key.id,
+ status: key.status === 'normal' ? 'enabled' : 'disable',
+ priority: index + 1
+ }))
+ }
+ }))
+ ]
+
+ const newEdges = [
+ ...modelData.map((model) => ({
+ id: `service-${model.id}`,
+ source: 'service',
+ target: model.id,
+ label: `apis(${model.api_count})`,
+ style: { stroke: '#ddd', cursor: 'pointer' },
+ labelStyle: { fill: '#3d46f2', fontSize: 12, cursor: 'pointer' },
+ labelBgStyle: { fill: 'white', fillOpacity: 0.8 },
+ labelBgPadding: [4, 2],
+ labelShowBg: true,
+ type: 'smoothstep',
+ markerEnd: { type: 'arrow' }
+ })),
+ ...modelData.map((model) => ({
+ id: `${model.id}-keys`,
+ source: model.id,
+ type: 'smoothstep',
+ target: `${model.id}-keys`,
+ animated: true,
+ style: { stroke: '#ddd' }
+ }))
+ ]
+
+ setNodes(newNodes)
+ setEdges(newEdges)
+ }, [modelData])
const onConnect = useCallback((params: any) => setEdges((eds) => addEdge(params, eds)), [setEdges])
@@ -250,7 +383,6 @@ const Playground = () => {
(_: any, node: any) => {
if (node.type !== 'modelCard') return
- // Update positions of connected nodes during drag
setNodes((nds) => {
return nds.map((n) => {
if (n.type === 'keyCard' && n.id === `${node.id}-keys`) {
@@ -273,7 +405,6 @@ const Playground = () => {
(_: any, node: any) => {
if (node.type !== 'modelCard') return
- // Reorder nodes based on vertical position
setNodes((nds) => {
const modelNodes = nds.filter((n) => n.type === 'modelCard')
const sortedNodes = [...modelNodes].sort((a, b) => a.position.y - b.position.y)
@@ -307,18 +438,8 @@ const Playground = () => {
onNodeDrag={onNodeDrag}
onNodeDragStop={onNodeDragStop}
nodeTypes={nodeTypes}
- defaultEdgeOptions={{
- type: 'step',
- style: { stroke: '#000', strokeWidth: 2 },
- animated: true
- }}
fitView
- nodesDraggable={true}
- elementsSelectable={false}
- nodesConnectable={false}
- zoomOnScroll={false}
- zoomOnPinch={false}
- zoomOnDoubleClick={false}
+ attributionPosition="bottom-left"
/>