feat(agent): support selecting models from api with calculated prices
parent
109d9cc779
commit
7cf68160a8
11
src/api.ts
11
src/api.ts
|
|
@ -111,6 +111,17 @@ export interface ChatHistoryResp {
|
||||||
branches: Record<string, BranchInfo>;
|
branches: Record<string, BranchInfo>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AiModel {
|
||||||
|
model_name: string;
|
||||||
|
icon: string;
|
||||||
|
model_ratio: number;
|
||||||
|
completion_ratio: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ModelAPI = {
|
||||||
|
list: () => api.get<{ data: AiModel[] }>('/models').then((r) => r.data.data),
|
||||||
|
};
|
||||||
|
|
||||||
export const AgentAPI = {
|
export const AgentAPI = {
|
||||||
list: () => api.get<Agent[]>('/agents').then((r) => r.data),
|
list: () => api.get<Agent[]>('/agents').then((r) => r.data),
|
||||||
detail: (id: string) => api.get<Agent>(`/agents/${id}`).then((r) => r.data),
|
detail: (id: string) => api.get<Agent>(`/agents/${id}`).then((r) => r.data),
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from 'react';
|
||||||
import { Button, Form, Input, InputNumber, Modal, Upload, App as AntApp, List, Popconfirm, Tag, Switch, Select, Collapse } from 'antd';
|
import { Button, Form, Input, InputNumber, Modal, Upload, App as AntApp, List, Popconfirm, Tag, Switch, Select, Collapse } from 'antd';
|
||||||
import type { UploadFile } from 'antd/es/upload/interface';
|
import type { UploadFile } from 'antd/es/upload/interface';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import { Agent, AgentAPI, ImageAPI, KnowledgeStatus, SkillType, Team, TeamAPI } from '../api';
|
import { Agent, AgentAPI, ImageAPI, KnowledgeStatus, SkillType, Team, TeamAPI, AiModel, ModelAPI } from '../api';
|
||||||
import SkillEditor from '../components/SkillEditor';
|
import SkillEditor from '../components/SkillEditor';
|
||||||
import McpPanel from '../components/McpPanel';
|
import McpPanel from '../components/McpPanel';
|
||||||
import ChatPreview from '../components/ChatPreview';
|
import ChatPreview from '../components/ChatPreview';
|
||||||
|
|
@ -37,6 +37,7 @@ export default function AgentEditor() {
|
||||||
const [agent, setAgent] = useState<Agent | null>(null);
|
const [agent, setAgent] = useState<Agent | null>(null);
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
const [teams, setTeams] = useState<Team[]>([]);
|
const [teams, setTeams] = useState<Team[]>([]);
|
||||||
|
const [models, setModels] = useState<AiModel[]>([]);
|
||||||
const [autoSaveStatus, setAutoSaveStatus] = useState<'saved' | 'saving' | 'error'>('saved');
|
const [autoSaveStatus, setAutoSaveStatus] = useState<'saved' | 'saving' | 'error'>('saved');
|
||||||
const [initModalOpen, setInitModalOpen] = useState(isNew);
|
const [initModalOpen, setInitModalOpen] = useState(isNew);
|
||||||
const [selectedAvatar, setSelectedAvatar] = useState(DEFAULT_AVATAR);
|
const [selectedAvatar, setSelectedAvatar] = useState(DEFAULT_AVATAR);
|
||||||
|
|
@ -74,6 +75,9 @@ export default function AgentEditor() {
|
||||||
TeamAPI.list()
|
TeamAPI.list()
|
||||||
.then(setTeams)
|
.then(setTeams)
|
||||||
.catch(() => setTeams([]));
|
.catch(() => setTeams([]));
|
||||||
|
ModelAPI.list()
|
||||||
|
.then(setModels)
|
||||||
|
.catch(() => setModels([]));
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
setInitModalOpen(true);
|
setInitModalOpen(true);
|
||||||
setSelectedAvatar(DEFAULT_AVATAR);
|
setSelectedAvatar(DEFAULT_AVATAR);
|
||||||
|
|
@ -475,9 +479,29 @@ export default function AgentEditor() {
|
||||||
label="模型"
|
label="模型"
|
||||||
className="mb-0"
|
className="mb-0"
|
||||||
>
|
>
|
||||||
<Input
|
<Select
|
||||||
placeholder="默认模型"
|
placeholder="选择模型"
|
||||||
style={{ borderRadius: 12, height: 42 }}
|
style={{ height: 42 }}
|
||||||
|
dropdownStyle={{ borderRadius: 12 }}
|
||||||
|
options={models.map((m) => {
|
||||||
|
const inputPrice = 2 * m.model_ratio;
|
||||||
|
const outputPrice = inputPrice * m.completion_ratio;
|
||||||
|
return {
|
||||||
|
value: m.model_name,
|
||||||
|
label: (
|
||||||
|
<div className="flex items-center justify-between w-full py-1">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<img src={m.icon} alt={m.model_name} className="w-5 h-5 object-contain rounded" />
|
||||||
|
<span className="font-medium text-gray-800">{m.model_name}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col items-end justify-center text-[10px] text-gray-400">
|
||||||
|
<span>输入: ${inputPrice.toFixed(2)}/M</span>
|
||||||
|
<span>输出: ${outputPrice.toFixed(2)}/M</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue