feat(admin): 添加网络代理配置和钱包交易功能

- 在管理面板中集成网络代理配置显示和平台代理设置
- 添加钱包摘要和交易列表API接口及数据管理
- 实现SSE流式响应中的错误处理机制
- 添加全局HTTP代理环境变量配置支持
- 更新平台表单以支持代理模式选择和自定义代理地址
- 集成钱包交易查询过滤和分页功能
- 优化API错误详情解析和显示格式
This commit is contained in:
2026-05-11 23:02:10 +08:00
parent c992f1de60
commit f550c0acd5
30 changed files with 1455 additions and 76 deletions
+21 -13
View File
@@ -2,8 +2,10 @@ import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react';
import {
AssistantRuntimeProvider,
ComposerPrimitive,
ErrorPrimitive,
MessagePrimitive,
ThreadPrimitive,
useMessage,
useMessagePartText,
useLocalRuntime,
useThread,
@@ -19,11 +21,12 @@ import { mermaid } from '@streamdown/mermaid';
import type { GatewayApiKey, GatewayTask, PlatformModel } from '@easyai-ai-gateway/contracts';
import { Bot, ChevronDown, Image as ImageIcon, MessageSquarePlus, Paperclip, Send, Sparkles, Video } from 'lucide-react';
import { Badge, Button, Select, Textarea } from '../components/ui';
import { createImageGenerationTask, createVideoGenerationTask, getTask, streamChatCompletionText } from '../api';
import { GatewayApiError, createImageGenerationTask, createVideoGenerationTask, getTask, streamChatCompletionText } from '../api';
import type { PlaygroundMode } from '../types';
import {
defaultMediaGenerationSettings,
deriveMediaModelCapabilities,
gatewayTaskErrorText,
mediaRequestPayload,
MediaSettingsPopover,
MediaTaskBoard,
@@ -172,7 +175,7 @@ export function PlaygroundPage(props: {
.then((detail) => {
if (!isMountedRef.current) return;
setMediaRuns((current) => updateMediaRun(current, run.localId, {
error: detail.error,
error: gatewayTaskErrorText(detail, '任务执行失败'),
status: detail.status,
task: detail,
}));
@@ -240,7 +243,7 @@ export function PlaygroundPage(props: {
setMediaRuns((current) => updateMediaRun(current, localId, { status: response.task.status, task: response.task }));
const detail = await pollTaskUntilSettled(credential, response.task);
setMediaRuns((current) => updateMediaRun(current, localId, {
error: detail.error,
error: gatewayTaskErrorText(detail, '任务执行失败'),
status: detail.status,
task: detail,
}));
@@ -453,13 +456,13 @@ function AssistantChatPlayground(props: {
async *run({ abortSignal, messages }) {
if (!props.token) {
props.onLogin();
throw new Error('请先登录后再测试模型。');
throw new GatewayApiError('请先登录后再测试模型。');
}
if (!activeApiKeySecret) {
throw new Error('请选择可用于测试的 API Key;如果列表为空,请刷新或重新创建一个 Key。');
throw new GatewayApiError('请选择可用于测试的 API Key;如果列表为空,请刷新或重新创建一个 Key。');
}
if (!props.selectedModel) {
throw new Error('当前没有可用的大模型,请确认用户组权限或平台模型配置。');
throw new GatewayApiError('当前没有可用的大模型,请确认用户组权限或平台模型配置。');
}
let text = '';
for await (const delta of streamChatCompletionText(
@@ -696,6 +699,8 @@ function AssistantChatComposer(props: {
}
function AssistantMessage() {
const hasError = useMessage((state) => state.status?.type === 'incomplete' && state.status.reason === 'error');
return (
<MessagePrimitive.Root className="assistantMessage">
<MessagePrimitive.If user>
@@ -704,16 +709,19 @@ function AssistantMessage() {
</div>
</MessagePrimitive.If>
<MessagePrimitive.If assistant>
<div className="assistantBubble assistant">
<div className={hasError ? 'assistantBubble assistant error' : 'assistantBubble assistant'}>
<MessagePrimitive.Parts components={{ Text: AssistantMarkdownText }} />
<MessagePrimitive.If hasContent={false}>
<span className="assistantTyping">...</span>
</MessagePrimitive.If>
<MessagePrimitive.Error>
<strong></strong>
<ErrorPrimitive.Message className="assistantErrorMessage" />
</MessagePrimitive.Error>
{!hasError && (
<MessagePrimitive.If hasContent={false}>
<span className="assistantTyping">...</span>
</MessagePrimitive.If>
)}
</div>
</MessagePrimitive.If>
<MessagePrimitive.Error>
<div className="assistantBubble error"></div>
</MessagePrimitive.Error>
</MessagePrimitive.Root>
);
}