feat: 自托管 AI Gateway 运维管理 SKILL
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { useEffect, useMemo, type FormEvent } from 'react';
|
||||
import type { GatewayApiKey, GatewayTask } from '@easyai-ai-gateway/contracts';
|
||||
import { BookOpen, KeyRound, Play, Search, Send } from 'lucide-react';
|
||||
import { useEffect, useMemo, useState, type FormEvent } from 'react';
|
||||
import type { GatewayApiKey, GatewaySkillBundleMetadata, GatewayTask } from '@easyai-ai-gateway/contracts';
|
||||
import { BookOpen, Download, ExternalLink, FileJson, KeyRound, Play, Search, Send, Wrench } from 'lucide-react';
|
||||
import { Badge, Button, Select, Textarea } from '../components/ui';
|
||||
import { getOpsManagementSkillMetadata, resolveApiAssetUrl } from '../api';
|
||||
import type { ApiDocSection, LoadState, TaskForm, TaskKind } from '../types';
|
||||
import { ApiKeySelect, apiKeyNoticeText, resolveSelectedApiKeyId } from './playground-shared';
|
||||
|
||||
@@ -35,6 +36,17 @@ const taskKindOptions = [
|
||||
['images.edits', '图像编辑'],
|
||||
] as const;
|
||||
|
||||
const defaultOpsSkillMetadata: GatewaySkillBundleMetadata = {
|
||||
name: 'ai-gateway-ops-management',
|
||||
version: '',
|
||||
displayName: 'AI Gateway 运维管理',
|
||||
modules: ['model-runtime'],
|
||||
fileName: 'ai-gateway-ops-management.zip',
|
||||
downloadPath: '/api/v1/public/skills/ai-gateway-ops-management/download',
|
||||
apiDocsJsonPath: '/api-docs-json',
|
||||
apiDocsYamlPath: '/api-docs-yaml',
|
||||
};
|
||||
|
||||
export function ApiDocsPage(props: {
|
||||
activeDocSection: ApiDocSection;
|
||||
apiKeySecretsById: Record<string, string>;
|
||||
@@ -53,6 +65,7 @@ export function ApiDocsPage(props: {
|
||||
onTaskFormChange: (value: TaskForm) => void;
|
||||
}) {
|
||||
const current = docs.find((item) => item.key === props.activeDocSection) ?? docs[0];
|
||||
const [opsSkillMetadata, setOpsSkillMetadata] = useState<GatewaySkillBundleMetadata>(defaultOpsSkillMetadata);
|
||||
const isFileDoc = current.key === 'files';
|
||||
const apiKeyNotice = apiKeyNoticeText(props.apiKeys, props.apiKeySecretsById);
|
||||
const activeApiKeyId = resolveSelectedApiKeyId(props.apiKeys, props.apiKeySecretsById, props.selectedApiKeyId);
|
||||
@@ -64,6 +77,20 @@ export function ApiDocsPage(props: {
|
||||
}
|
||||
}, [current.kind, props.taskForm.kind]);
|
||||
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
getOpsManagementSkillMetadata()
|
||||
.then((metadata) => {
|
||||
if (active) setOpsSkillMetadata(metadata);
|
||||
})
|
||||
.catch(() => {
|
||||
// Keep stable public fallback paths visible when metadata is temporarily unavailable.
|
||||
});
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
function handleSubmit(event: FormEvent<HTMLFormElement>) {
|
||||
if (!props.canRun) {
|
||||
event.preventDefault();
|
||||
@@ -123,6 +150,8 @@ export function ApiDocsPage(props: {
|
||||
</div>
|
||||
<p className="docsLead">{current.lead}</p>
|
||||
|
||||
<OpsManagementSkillCard metadata={opsSkillMetadata} />
|
||||
|
||||
<section className="paramCard">
|
||||
<header>
|
||||
<h2>Header 参数</h2>
|
||||
@@ -215,6 +244,64 @@ export function ApiDocsPage(props: {
|
||||
);
|
||||
}
|
||||
|
||||
function OpsManagementSkillCard(props: { metadata: GatewaySkillBundleMetadata }) {
|
||||
const modules = props.metadata.modules.map(skillModuleLabel).join('、');
|
||||
return (
|
||||
<section className="agentResourceCard">
|
||||
<div className="agentResourceIcon">
|
||||
<Wrench size={22} />
|
||||
</div>
|
||||
<div className="agentResourceContent">
|
||||
<div className="agentResourceTitle">
|
||||
<div>
|
||||
<p className="eyebrow">Agent 运维资源</p>
|
||||
<h2>{props.metadata.displayName}</h2>
|
||||
</div>
|
||||
<Badge variant="outline">{props.metadata.version ? `v${props.metadata.version}` : '最新版本'}</Badge>
|
||||
</div>
|
||||
<p>用于让 Agent 安全操作厂商、基础模型、定价、运行策略、平台、平台模型和 universal 自定义平台。</p>
|
||||
<div className="agentResourceModules">
|
||||
<span>当前模块</span>
|
||||
<strong>{modules || '模型运行时'}</strong>
|
||||
</div>
|
||||
<div className="agentResourceActions">
|
||||
<Button asChild size="sm">
|
||||
<a href={resolveApiAssetUrl(props.metadata.downloadPath)}>
|
||||
<Download size={14} />
|
||||
下载 SKILL
|
||||
</a>
|
||||
</Button>
|
||||
<Button asChild size="sm" variant="secondary">
|
||||
<a href={resolveApiAssetUrl(props.metadata.apiDocsJsonPath)} rel="noreferrer" target="_blank">
|
||||
<FileJson size={14} />
|
||||
Swagger JSON
|
||||
</a>
|
||||
</Button>
|
||||
<Button asChild size="sm" variant="outline">
|
||||
<a href={resolveApiAssetUrl(props.metadata.apiDocsYamlPath)} rel="noreferrer" target="_blank">
|
||||
<ExternalLink size={14} />
|
||||
Swagger YAML
|
||||
</a>
|
||||
</Button>
|
||||
<a
|
||||
className="agentResourceMetadataLink"
|
||||
href={resolveApiAssetUrl('/api/v1/public/skills/ai-gateway-ops-management/metadata')}
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
查看 metadata
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function skillModuleLabel(module: string) {
|
||||
if (module === 'model-runtime') return '模型运行时';
|
||||
return module;
|
||||
}
|
||||
|
||||
function DocsGroup(props: {
|
||||
items: Array<{ active?: boolean; method?: string; onClick?: () => void; title: string }>;
|
||||
title: string;
|
||||
|
||||
Reference in New Issue
Block a user