feat: add ai gateway local core flow
This commit is contained in:
+197
-22
@@ -2,7 +2,9 @@ import { useEffect, useMemo, useState, type FormEvent } from 'react';
|
||||
import type {
|
||||
BaseModelCatalogItem,
|
||||
CatalogProvider,
|
||||
GatewayApiKey,
|
||||
GatewayTenant,
|
||||
GatewayTask,
|
||||
GatewayUser,
|
||||
IntegrationPlatform,
|
||||
PlatformModel,
|
||||
@@ -11,7 +13,12 @@ import type {
|
||||
UserGroup,
|
||||
} from '@easyai-ai-gateway/contracts';
|
||||
import {
|
||||
createApiKey,
|
||||
createChatTask,
|
||||
createPlatform,
|
||||
getTask,
|
||||
getHealth,
|
||||
listApiKeys,
|
||||
listBaseModels,
|
||||
listCatalogProviders,
|
||||
listModels,
|
||||
@@ -96,8 +103,6 @@ export function App() {
|
||||
email: '',
|
||||
password: '',
|
||||
displayName: '',
|
||||
tenantKey: '',
|
||||
tenantName: '',
|
||||
invitationCode: '',
|
||||
});
|
||||
const [health, setHealth] = useState<HealthResponse | null>(null);
|
||||
@@ -110,6 +115,22 @@ export function App() {
|
||||
const [tenants, setTenants] = useState<GatewayTenant[]>([]);
|
||||
const [users, setUsers] = useState<GatewayUser[]>([]);
|
||||
const [userGroups, setUserGroups] = useState<UserGroup[]>([]);
|
||||
const [apiKeys, setApiKeys] = useState<GatewayApiKey[]>([]);
|
||||
const [apiKeyForm, setApiKeyForm] = useState({ name: 'Local smoke key' });
|
||||
const [apiKeySecret, setApiKeySecret] = useState('');
|
||||
const [platformForm, setPlatformForm] = useState({
|
||||
provider: 'openai',
|
||||
platformKey: 'openai-simulation',
|
||||
name: 'OpenAI Simulation',
|
||||
baseUrl: 'https://api.openai.com/v1',
|
||||
});
|
||||
const [taskForm, setTaskForm] = useState({
|
||||
model: 'gpt-4o-mini',
|
||||
prompt: '用一句话确认 AI Gateway simulation 链路正常。',
|
||||
});
|
||||
const [taskResult, setTaskResult] = useState<GatewayTask | null>(null);
|
||||
const [coreState, setCoreState] = useState<LoadState>('idle');
|
||||
const [coreMessage, setCoreMessage] = useState('');
|
||||
const [state, setState] = useState<LoadState>('idle');
|
||||
const [error, setError] = useState('');
|
||||
|
||||
@@ -151,6 +172,7 @@ export function App() {
|
||||
tenantResponse,
|
||||
userResponse,
|
||||
userGroupResponse,
|
||||
apiKeyResponse,
|
||||
] = await Promise.all([
|
||||
listPlatforms(nextToken),
|
||||
listModels(nextToken),
|
||||
@@ -161,6 +183,7 @@ export function App() {
|
||||
listTenants(nextToken),
|
||||
listUsers(nextToken),
|
||||
listUserGroups(nextToken),
|
||||
listApiKeys(nextToken),
|
||||
]);
|
||||
setPlatforms(platformResponse.items);
|
||||
setModels(modelResponse.items);
|
||||
@@ -171,6 +194,7 @@ export function App() {
|
||||
setTenants(tenantResponse.items);
|
||||
setUsers(userResponse.items);
|
||||
setUserGroups(userGroupResponse.items);
|
||||
setApiKeys(apiKeyResponse.items);
|
||||
setState('ready');
|
||||
} catch (err) {
|
||||
setState('error');
|
||||
@@ -229,6 +253,68 @@ export function App() {
|
||||
setTenants([]);
|
||||
setUsers([]);
|
||||
setUserGroups([]);
|
||||
setApiKeys([]);
|
||||
setApiKeySecret('');
|
||||
setTaskResult(null);
|
||||
setCoreMessage('');
|
||||
}
|
||||
|
||||
async function submitAPIKey(event: FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault();
|
||||
setCoreState('loading');
|
||||
setCoreMessage('');
|
||||
try {
|
||||
const response = await createApiKey(token, { name: apiKeyForm.name, scopes: ['chat', 'image', 'video'] });
|
||||
setApiKeySecret(response.secret);
|
||||
setApiKeys((current) => [response.apiKey, ...current.filter((item) => item.id !== response.apiKey.id)]);
|
||||
setCoreState('ready');
|
||||
setCoreMessage('API Key 已创建,secret 仅展示一次。');
|
||||
} catch (err) {
|
||||
setCoreState('error');
|
||||
setCoreMessage(err instanceof Error ? err.message : '创建 API Key 失败');
|
||||
}
|
||||
}
|
||||
|
||||
async function submitPlatform(event: FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault();
|
||||
setCoreState('loading');
|
||||
setCoreMessage('');
|
||||
try {
|
||||
const platform = await createPlatform(token, {
|
||||
...platformForm,
|
||||
authType: 'bearer',
|
||||
credentials: { mode: 'simulation' },
|
||||
config: { testMode: true },
|
||||
});
|
||||
setPlatforms((current) => [platform, ...current.filter((item) => item.id !== platform.id)]);
|
||||
setCoreState('ready');
|
||||
setCoreMessage('平台已创建,当前阶段平台凭证仅全局管理员可配置。');
|
||||
} catch (err) {
|
||||
setCoreState('error');
|
||||
setCoreMessage(err instanceof Error ? err.message : '创建平台失败');
|
||||
}
|
||||
}
|
||||
|
||||
async function submitTask(event: FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault();
|
||||
const credential = apiKeySecret || token;
|
||||
setCoreState('loading');
|
||||
setCoreMessage('');
|
||||
try {
|
||||
const response = await createChatTask(credential, {
|
||||
model: taskForm.model,
|
||||
runMode: 'simulation',
|
||||
simulation: true,
|
||||
messages: [{ role: 'user', content: taskForm.prompt }],
|
||||
});
|
||||
const detail = await getTask(credential, response.task.id);
|
||||
setTaskResult(detail);
|
||||
setCoreState('ready');
|
||||
setCoreMessage(apiKeySecret ? '任务已通过本地 API Key 完成 simulation。' : '任务已通过当前 Access Token 完成 simulation。');
|
||||
} catch (err) {
|
||||
setCoreState('error');
|
||||
setCoreMessage(err instanceof Error ? err.message : '测试任务失败');
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -278,6 +364,23 @@ export function App() {
|
||||
</button>
|
||||
</section>
|
||||
|
||||
<CoreFlowPanel
|
||||
apiKeyForm={apiKeyForm}
|
||||
apiKeys={apiKeys}
|
||||
apiKeySecret={apiKeySecret}
|
||||
coreMessage={coreMessage}
|
||||
coreState={coreState}
|
||||
platformForm={platformForm}
|
||||
taskForm={taskForm}
|
||||
taskResult={taskResult}
|
||||
onAPIKeyFormChange={setApiKeyForm}
|
||||
onPlatformFormChange={setPlatformForm}
|
||||
onSubmitAPIKey={submitAPIKey}
|
||||
onSubmitPlatform={submitPlatform}
|
||||
onSubmitTask={submitTask}
|
||||
onTaskFormChange={setTaskForm}
|
||||
/>
|
||||
|
||||
<Dashboard
|
||||
baseModels={baseModels}
|
||||
models={models}
|
||||
@@ -302,8 +405,6 @@ function AuthPanel(props: {
|
||||
email: string;
|
||||
password: string;
|
||||
displayName: string;
|
||||
tenantKey: string;
|
||||
tenantName: string;
|
||||
invitationCode: string;
|
||||
};
|
||||
state: LoadState;
|
||||
@@ -315,8 +416,6 @@ function AuthPanel(props: {
|
||||
email: string;
|
||||
password: string;
|
||||
displayName: string;
|
||||
tenantKey: string;
|
||||
tenantName: string;
|
||||
invitationCode: string;
|
||||
}) => void;
|
||||
onSubmitExternalToken: (event: FormEvent<HTMLFormElement>) => void;
|
||||
@@ -414,22 +513,6 @@ function AuthPanel(props: {
|
||||
placeholder="至少 8 位"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span>租户 Key</span>
|
||||
<input
|
||||
value={props.registerForm.tenantKey}
|
||||
onChange={(event) => props.onRegisterChange({ ...props.registerForm, tenantKey: event.target.value })}
|
||||
placeholder="team-a"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span>租户名称</span>
|
||||
<input
|
||||
value={props.registerForm.tenantName}
|
||||
onChange={(event) => props.onRegisterChange({ ...props.registerForm, tenantName: event.target.value })}
|
||||
placeholder="Team A"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span>邀请码</span>
|
||||
<input
|
||||
@@ -464,6 +547,98 @@ function AuthPanel(props: {
|
||||
);
|
||||
}
|
||||
|
||||
function CoreFlowPanel(props: {
|
||||
apiKeyForm: { name: string };
|
||||
apiKeys: GatewayApiKey[];
|
||||
apiKeySecret: string;
|
||||
coreMessage: string;
|
||||
coreState: LoadState;
|
||||
platformForm: { provider: string; platformKey: string; name: string; baseUrl: string };
|
||||
taskForm: { model: string; prompt: string };
|
||||
taskResult: GatewayTask | null;
|
||||
onAPIKeyFormChange: (value: { name: string }) => void;
|
||||
onPlatformFormChange: (value: { provider: string; platformKey: string; name: string; baseUrl: string }) => void;
|
||||
onSubmitAPIKey: (event: FormEvent<HTMLFormElement>) => void;
|
||||
onSubmitPlatform: (event: FormEvent<HTMLFormElement>) => void;
|
||||
onSubmitTask: (event: FormEvent<HTMLFormElement>) => void;
|
||||
onTaskFormChange: (value: { model: string; prompt: string }) => void;
|
||||
}) {
|
||||
return (
|
||||
<section className="corePanel" aria-label="核心链路验证">
|
||||
<div className="sectionHeader">
|
||||
<div>
|
||||
<p className="eyebrow">Smoke Flow</p>
|
||||
<h2>核心链路验证</h2>
|
||||
</div>
|
||||
<span>{props.coreState === 'loading' ? '运行中' : '本地闭环'}</span>
|
||||
</div>
|
||||
|
||||
<div className="coreGrid">
|
||||
<form className="inlineForm" onSubmit={props.onSubmitAPIKey}>
|
||||
<h3>1. API Key</h3>
|
||||
<label>
|
||||
<span>名称</span>
|
||||
<input value={props.apiKeyForm.name} onChange={(event) => props.onAPIKeyFormChange({ name: event.target.value })} />
|
||||
</label>
|
||||
<button type="submit" disabled={props.coreState === 'loading'}>
|
||||
创建 API Key
|
||||
</button>
|
||||
<p className="formHint">已创建 {props.apiKeys.length} 个 Key</p>
|
||||
{props.apiKeySecret && <code className="secretBox">{props.apiKeySecret}</code>}
|
||||
</form>
|
||||
|
||||
<form className="inlineForm" onSubmit={props.onSubmitPlatform}>
|
||||
<h3>2. 平台</h3>
|
||||
<label>
|
||||
<span>Provider</span>
|
||||
<input value={props.platformForm.provider} onChange={(event) => props.onPlatformFormChange({ ...props.platformForm, provider: event.target.value })} />
|
||||
</label>
|
||||
<label>
|
||||
<span>平台 Key</span>
|
||||
<input value={props.platformForm.platformKey} onChange={(event) => props.onPlatformFormChange({ ...props.platformForm, platformKey: event.target.value })} />
|
||||
</label>
|
||||
<label>
|
||||
<span>名称</span>
|
||||
<input value={props.platformForm.name} onChange={(event) => props.onPlatformFormChange({ ...props.platformForm, name: event.target.value })} />
|
||||
</label>
|
||||
<label>
|
||||
<span>Base URL</span>
|
||||
<input value={props.platformForm.baseUrl} onChange={(event) => props.onPlatformFormChange({ ...props.platformForm, baseUrl: event.target.value })} />
|
||||
</label>
|
||||
<button type="submit" disabled={props.coreState === 'loading'}>
|
||||
创建平台
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<form className="inlineForm" onSubmit={props.onSubmitTask}>
|
||||
<h3>3. Simulation 任务</h3>
|
||||
<label>
|
||||
<span>模型</span>
|
||||
<input value={props.taskForm.model} onChange={(event) => props.onTaskFormChange({ ...props.taskForm, model: event.target.value })} />
|
||||
</label>
|
||||
<label>
|
||||
<span>Prompt</span>
|
||||
<input value={props.taskForm.prompt} onChange={(event) => props.onTaskFormChange({ ...props.taskForm, prompt: event.target.value })} />
|
||||
</label>
|
||||
<button type="submit" disabled={props.coreState === 'loading'}>
|
||||
生成测试任务
|
||||
</button>
|
||||
{props.taskResult && (
|
||||
<div className="resultBox">
|
||||
<div>
|
||||
<span className="statusPill">{props.taskResult.status}</span>
|
||||
<strong>{props.taskResult.model}</strong>
|
||||
</div>
|
||||
<pre>{JSON.stringify(props.taskResult.result ?? {}, null, 2)}</pre>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
</div>
|
||||
{props.coreMessage && <p className="coreMessage" data-error={props.coreState === 'error'}>{props.coreMessage}</p>}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function Dashboard(props: {
|
||||
baseModels: BaseModelCatalogItem[];
|
||||
models: PlatformModel[];
|
||||
|
||||
Reference in New Issue
Block a user