iconsulting/packages/admin-client/src/features/llm-gateway/presentation/components/InjectionRulesTab.tsx

100 lines
3.5 KiB
TypeScript

import { useState } from 'react';
import { Table, Button, Modal, Form, Input, Select, Switch, Popconfirm, Tag } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { useInjectionRules, useCreateInjectionRule, useDeleteInjectionRule, useToggleInjectionRule } from '../../application/useLLMGateway';
import type { InjectionRule } from '../../infrastructure/llm-gateway.api';
const { TextArea } = Input;
export function InjectionRulesTab() {
const { data, isLoading } = useInjectionRules();
const createMutation = useCreateInjectionRule();
const deleteMutation = useDeleteInjectionRule();
const toggleMutation = useToggleInjectionRule();
const [createOpen, setCreateOpen] = useState(false);
const [form] = Form.useForm();
const handleCreate = async () => {
const values = await form.validateFields();
await createMutation.mutateAsync(values);
setCreateOpen(false);
form.resetFields();
};
const columns = [
{ title: '名称', dataIndex: 'name', key: 'name' },
{
title: '位置',
dataIndex: 'position',
key: 'position',
render: (v: string) => <Tag color={v === 'prepend' ? 'blue' : 'green'}>{v === 'prepend' ? '前置' : '追加'}</Tag>,
},
{
title: '内容预览',
dataIndex: 'content',
key: 'content',
ellipsis: true,
width: 300,
},
{
title: '匹配模型',
dataIndex: 'matchModels',
key: 'matchModels',
render: (v: string[]) => v?.includes('*') ? <Tag></Tag> : v?.map((m: string) => <Tag key={m}>{m}</Tag>),
},
{
title: '状态',
dataIndex: 'enabled',
key: 'enabled',
render: (v: boolean, record: InjectionRule) => (
<Switch checked={v} size="small" onChange={() => toggleMutation.mutate(record.id)} />
),
},
{
title: '操作',
key: 'actions',
render: (_: any, record: InjectionRule) => (
<Popconfirm title="确认删除?" onConfirm={() => deleteMutation.mutate(record.id)}>
<Button type="link" danger size="small"></Button>
</Popconfirm>
),
},
];
return (
<>
<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 16 }}>
<Button type="primary" icon={<PlusOutlined />} onClick={() => setCreateOpen(true)}>
</Button>
</div>
<Table columns={columns} dataSource={data?.items} rowKey="id" loading={isLoading} size="small" pagination={false} />
<Modal
title="创建注入规则"
open={createOpen}
onOk={handleCreate}
onCancel={() => setCreateOpen(false)}
confirmLoading={createMutation.isPending}
width={600}
>
<Form form={form} layout="vertical">
<Form.Item name="name" label="规则名称" rules={[{ required: true }]}>
<Input placeholder="例如: 合规声明" />
</Form.Item>
<Form.Item name="position" label="注入位置" initialValue="append">
<Select options={[{ label: '追加到 system prompt 末尾', value: 'append' }, { label: '前置到 system prompt 开头', value: 'prepend' }]} />
</Form.Item>
<Form.Item name="content" label="注入内容" rules={[{ required: true }]}>
<TextArea rows={6} placeholder="输入要注入到 system prompt 中的监管内容..." />
</Form.Item>
<Form.Item name="description" label="描述">
<Input placeholder="规则说明(可选)" />
</Form.Item>
</Form>
</Modal>
</>
);
}