format: 格式化代码
This commit is contained in:
parent
e07e88ae49
commit
4df242e63b
@ -27,9 +27,9 @@ from .sandbox import (
|
|||||||
get_sandbox_base_port,
|
get_sandbox_base_port,
|
||||||
get_sandbox_host,
|
get_sandbox_host,
|
||||||
get_sandbox_image,
|
get_sandbox_image,
|
||||||
|
get_sandbox_provider,
|
||||||
get_sandbox_provisioner_url,
|
get_sandbox_provisioner_url,
|
||||||
get_sandbox_security_opts,
|
get_sandbox_security_opts,
|
||||||
get_sandbox_provider,
|
|
||||||
)
|
)
|
||||||
from .skills_backend import SelectedSkillsReadonlyBackend
|
from .skills_backend import SelectedSkillsReadonlyBackend
|
||||||
|
|
||||||
|
|||||||
@ -4,8 +4,8 @@ from deepagents.backends import CompositeBackend, StateBackend
|
|||||||
|
|
||||||
from yuxi.agents.middlewares.skills_middleware import normalize_selected_skills
|
from yuxi.agents.middlewares.skills_middleware import normalize_selected_skills
|
||||||
|
|
||||||
from .skills_backend import SelectedSkillsReadonlyBackend
|
|
||||||
from .sandbox import get_sandbox_provider
|
from .sandbox import get_sandbox_provider
|
||||||
|
from .skills_backend import SelectedSkillsReadonlyBackend
|
||||||
|
|
||||||
|
|
||||||
def _get_visible_skills_from_runtime(runtime) -> list[str]:
|
def _get_visible_skills_from_runtime(runtime) -> list[str]:
|
||||||
|
|||||||
@ -16,10 +16,7 @@
|
|||||||
- docker_api: Docker API 调用封装
|
- docker_api: Docker API 调用封装
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .sandbox_executor import YuxiSandboxBackend
|
from .path_security import normalize_virtual_path
|
||||||
from .sandbox_info import SandboxInfo
|
|
||||||
from .sandbox_provisioner import YuxiSandboxProvider, get_sandbox_provider
|
|
||||||
from .sandbox_provisioner_base import SandboxBackend
|
|
||||||
from .sandbox_config import (
|
from .sandbox_config import (
|
||||||
IDLE_CHECK_INTERVAL,
|
IDLE_CHECK_INTERVAL,
|
||||||
LARGE_TOOL_RESULTS_DIR,
|
LARGE_TOOL_RESULTS_DIR,
|
||||||
@ -39,9 +36,12 @@ from .sandbox_config import (
|
|||||||
get_sandbox_provisioner_url,
|
get_sandbox_provisioner_url,
|
||||||
get_sandbox_security_opts,
|
get_sandbox_security_opts,
|
||||||
)
|
)
|
||||||
|
from .sandbox_executor import YuxiSandboxBackend
|
||||||
|
from .sandbox_info import SandboxInfo
|
||||||
from .sandbox_local_container import LocalContainerBackend
|
from .sandbox_local_container import LocalContainerBackend
|
||||||
|
from .sandbox_provisioner import YuxiSandboxProvider, get_sandbox_provider
|
||||||
|
from .sandbox_provisioner_base import SandboxBackend
|
||||||
from .sandbox_remote import RemoteSandboxBackend
|
from .sandbox_remote import RemoteSandboxBackend
|
||||||
from .path_security import normalize_virtual_path
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
# Executor
|
# Executor
|
||||||
|
|||||||
@ -9,9 +9,9 @@ import urllib.parse
|
|||||||
from yuxi.utils.logging_config import logger
|
from yuxi.utils.logging_config import logger
|
||||||
|
|
||||||
from .docker_api import docker_api_request
|
from .docker_api import docker_api_request
|
||||||
from .sandbox_provisioner_base import SandboxBackend
|
|
||||||
from .sandbox_config import get_sandbox_host, get_sandbox_security_opts
|
from .sandbox_config import get_sandbox_host, get_sandbox_security_opts
|
||||||
from .sandbox_info import SandboxInfo
|
from .sandbox_info import SandboxInfo
|
||||||
|
from .sandbox_provisioner_base import SandboxBackend
|
||||||
|
|
||||||
|
|
||||||
class LocalContainerBackend(SandboxBackend):
|
class LocalContainerBackend(SandboxBackend):
|
||||||
|
|||||||
@ -14,10 +14,6 @@ from yuxi import config as sys_config
|
|||||||
from yuxi.services.skill_service import get_skills_root_dir
|
from yuxi.services.skill_service import get_skills_root_dir
|
||||||
from yuxi.utils.logging_config import logger
|
from yuxi.utils.logging_config import logger
|
||||||
|
|
||||||
from .sandbox_local_container import LocalContainerBackend
|
|
||||||
from .sandbox_remote import RemoteSandboxBackend
|
|
||||||
from .sandbox_executor import YuxiSandboxBackend
|
|
||||||
from .sandbox_provisioner_base import SandboxBackend
|
|
||||||
from .sandbox_config import (
|
from .sandbox_config import (
|
||||||
IDLE_CHECK_INTERVAL,
|
IDLE_CHECK_INTERVAL,
|
||||||
LARGE_TOOL_RESULTS_DIR,
|
LARGE_TOOL_RESULTS_DIR,
|
||||||
@ -34,7 +30,11 @@ from .sandbox_config import (
|
|||||||
get_sandbox_image,
|
get_sandbox_image,
|
||||||
get_sandbox_provisioner_url,
|
get_sandbox_provisioner_url,
|
||||||
)
|
)
|
||||||
|
from .sandbox_executor import YuxiSandboxBackend
|
||||||
from .sandbox_info import SandboxInfo
|
from .sandbox_info import SandboxInfo
|
||||||
|
from .sandbox_local_container import LocalContainerBackend
|
||||||
|
from .sandbox_provisioner_base import SandboxBackend
|
||||||
|
from .sandbox_remote import RemoteSandboxBackend
|
||||||
|
|
||||||
|
|
||||||
class YuxiSandboxProvider:
|
class YuxiSandboxProvider:
|
||||||
@ -259,9 +259,7 @@ class YuxiSandboxProvider:
|
|||||||
self._last_activity.pop(sandbox_id, None)
|
self._last_activity.pop(sandbox_id, None)
|
||||||
self._warm_pool.pop(sandbox_id, None)
|
self._warm_pool.pop(sandbox_id, None)
|
||||||
thread_ids = [
|
thread_ids = [
|
||||||
thread_id
|
thread_id for thread_id, mapped_id in self._thread_sandboxes.items() if mapped_id == sandbox_id
|
||||||
for thread_id, mapped_id in self._thread_sandboxes.items()
|
|
||||||
if mapped_id == sandbox_id
|
|
||||||
]
|
]
|
||||||
for thread_id in thread_ids:
|
for thread_id in thread_ids:
|
||||||
del self._thread_sandboxes[thread_id]
|
del self._thread_sandboxes[thread_id]
|
||||||
|
|||||||
@ -13,17 +13,13 @@ class SandboxBackend(ABC):
|
|||||||
thread_id: str,
|
thread_id: str,
|
||||||
sandbox_id: str,
|
sandbox_id: str,
|
||||||
extra_mounts: list[tuple[str, str, bool]] | None = None,
|
extra_mounts: list[tuple[str, str, bool]] | None = None,
|
||||||
) -> SandboxInfo:
|
) -> SandboxInfo: ...
|
||||||
...
|
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def destroy(self, info: SandboxInfo) -> None:
|
def destroy(self, info: SandboxInfo) -> None: ...
|
||||||
...
|
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def is_alive(self, info: SandboxInfo) -> bool:
|
def is_alive(self, info: SandboxInfo) -> bool: ...
|
||||||
...
|
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def discover(self, sandbox_id: str) -> SandboxInfo | None:
|
def discover(self, sandbox_id: str) -> SandboxInfo | None: ...
|
||||||
...
|
|
||||||
|
|||||||
@ -6,8 +6,8 @@ import requests
|
|||||||
|
|
||||||
from yuxi.utils.logging_config import logger
|
from yuxi.utils.logging_config import logger
|
||||||
|
|
||||||
from .sandbox_provisioner_base import SandboxBackend
|
|
||||||
from .sandbox_info import SandboxInfo
|
from .sandbox_info import SandboxInfo
|
||||||
|
from .sandbox_provisioner_base import SandboxBackend
|
||||||
|
|
||||||
|
|
||||||
class RemoteSandboxBackend(SandboxBackend):
|
class RemoteSandboxBackend(SandboxBackend):
|
||||||
|
|||||||
@ -21,6 +21,7 @@ from yuxi.services.subagent_service import get_subagents_from_names
|
|||||||
|
|
||||||
from .prompt import PROMPT
|
from .prompt import PROMPT
|
||||||
|
|
||||||
|
|
||||||
def _create_fs_backend(rt):
|
def _create_fs_backend(rt):
|
||||||
"""创建文件存储后端(支持沙盒执行)
|
"""创建文件存储后端(支持沙盒执行)
|
||||||
|
|
||||||
|
|||||||
@ -39,11 +39,7 @@ class DeepAgent(BaseAgent):
|
|||||||
description = "具备规划、深度分析和子智能体协作能力的智能体,可以处理复杂的多步骤任务"
|
description = "具备规划、深度分析和子智能体协作能力的智能体,可以处理复杂的多步骤任务"
|
||||||
context_schema = DeepContext
|
context_schema = DeepContext
|
||||||
capabilities = ["file_upload", "files", "todo"] # 支持文件上传功能
|
capabilities = ["file_upload", "files", "todo"] # 支持文件上传功能
|
||||||
metadata = {
|
metadata = {"examples": ["调研一下多模态 GraphRAG 的相关论文"]}
|
||||||
"examples": [
|
|
||||||
"调研一下多模态 GraphRAG 的相关论文"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|||||||
@ -92,7 +92,7 @@ class BaseContext:
|
|||||||
default=sys_config.default_model,
|
default=sys_config.default_model,
|
||||||
metadata={
|
metadata={
|
||||||
"name": "子智能体的默认模型",
|
"name": "子智能体的默认模型",
|
||||||
"description": "子智能体的默认模型,会被子智能体的配置覆盖。",
|
"description": "为所有子智能体设置默认模型,可在各子智能体配置中单独覆盖。",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -404,9 +404,8 @@ class SkillsMiddleware(AgentMiddleware):
|
|||||||
return None
|
return None
|
||||||
pure = PurePosixPath(raw if raw.startswith("/") else f"/{raw}")
|
pure = PurePosixPath(raw if raw.startswith("/") else f"/{raw}")
|
||||||
parts = [p for p in pure.parts if p not in ("/", "")]
|
parts = [p for p in pure.parts if p not in ("/", "")]
|
||||||
if len(parts) == 3 and parts[0] == "skills" and parts[2] == "SKILL.md":
|
# 只支持 /mnt/skills/{slug}/SKILL.md 格式
|
||||||
slug = parts[1]
|
if len(parts) == 4 and parts[0] == "mnt" and parts[1] == "skills" and parts[3] == "SKILL.md":
|
||||||
elif len(parts) == 4 and parts[0] == "mnt" and parts[1] == "skills" and parts[3] == "SKILL.md":
|
|
||||||
slug = parts[2]
|
slug = parts[2]
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|||||||
@ -4,15 +4,15 @@ import asyncio
|
|||||||
|
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from yuxi.agents.context import BaseContext
|
|
||||||
from yuxi.agents.backends import (
|
from yuxi.agents.backends import (
|
||||||
create_agent_composite_backend,
|
create_agent_composite_backend,
|
||||||
resolve_sandbox_backend_async,
|
resolve_sandbox_backend_async,
|
||||||
)
|
)
|
||||||
|
from yuxi.agents.context import BaseContext
|
||||||
from yuxi.repositories.agent_config_repository import AgentConfigRepository
|
from yuxi.repositories.agent_config_repository import AgentConfigRepository
|
||||||
from yuxi.repositories.conversation_repository import ConversationRepository
|
from yuxi.repositories.conversation_repository import ConversationRepository
|
||||||
from yuxi.storage.postgres.models_business import User
|
|
||||||
from yuxi.services.conversation_service import require_user_conversation
|
from yuxi.services.conversation_service import require_user_conversation
|
||||||
|
from yuxi.storage.postgres.models_business import User
|
||||||
|
|
||||||
|
|
||||||
async def _resolve_filesystem_context(
|
async def _resolve_filesystem_context(
|
||||||
|
|||||||
@ -9,12 +9,11 @@ from urllib.parse import quote
|
|||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
from fastapi.responses import StreamingResponse
|
from fastapi.responses import StreamingResponse
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from yuxi.agents.backends.sandbox import SKILLS_PATH, USER_DATA_PATH
|
from yuxi.agents.backends.sandbox import SKILLS_PATH, USER_DATA_PATH
|
||||||
from yuxi.agents.backends.skills_backend import SelectedSkillsReadonlyBackend
|
from yuxi.agents.backends.skills_backend import SelectedSkillsReadonlyBackend
|
||||||
from yuxi.agents.middlewares.skills_middleware import normalize_selected_skills
|
from yuxi.agents.middlewares.skills_middleware import normalize_selected_skills
|
||||||
from yuxi.storage.postgres.models_business import User
|
|
||||||
from yuxi.services.filesystem_service import _resolve_filesystem_state
|
from yuxi.services.filesystem_service import _resolve_filesystem_state
|
||||||
|
from yuxi.storage.postgres.models_business import User
|
||||||
|
|
||||||
|
|
||||||
def _normalize_path(path: str | None) -> str:
|
def _normalize_path(path: str | None) -> str:
|
||||||
@ -117,9 +116,7 @@ async def list_viewer_filesystem_tree(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if normalized_path == "/":
|
if normalized_path == "/":
|
||||||
entries = [
|
entries = [{"path": f"{USER_DATA_PATH}/", "name": "user-data", "is_dir": True, "size": 0, "modified_at": ""}]
|
||||||
{"path": f"{USER_DATA_PATH}/", "name": "user-data", "is_dir": True, "size": 0, "modified_at": ""}
|
|
||||||
]
|
|
||||||
if selected_skills:
|
if selected_skills:
|
||||||
entries.append({"path": f"{SKILLS_PATH}/", "name": "skills", "is_dir": True, "size": 0, "modified_at": ""})
|
entries.append({"path": f"{SKILLS_PATH}/", "name": "skills", "is_dir": True, "size": 0, "modified_at": ""})
|
||||||
return {"entries": entries}
|
return {"entries": entries}
|
||||||
|
|||||||
@ -67,7 +67,7 @@ $images = @(
|
|||||||
"ghcr.io/astral-sh/uv:0.7.2",
|
"ghcr.io/astral-sh/uv:0.7.2",
|
||||||
"nginx:alpine",
|
"nginx:alpine",
|
||||||
"quay.io/coreos/etcd:v3.5.5",
|
"quay.io/coreos/etcd:v3.5.5",
|
||||||
"postgres:16"
|
"postgres:16",
|
||||||
"redis:7-alpine"
|
"redis:7-alpine"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -476,7 +476,9 @@ const isStreaming = computed(() => {
|
|||||||
})
|
})
|
||||||
const isProcessing = computed(() => isStreaming.value)
|
const isProcessing = computed(() => isStreaming.value)
|
||||||
const isSendButtonDisabled = computed(() => {
|
const isSendButtonDisabled = computed(() => {
|
||||||
return sendCooldownActive.value || (((!userInput.value || !currentAgent.value) && !isProcessing.value))
|
return (
|
||||||
|
sendCooldownActive.value || ((!userInput.value || !currentAgent.value) && !isProcessing.value)
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const startSendCooldown = () => {
|
const startSendCooldown = () => {
|
||||||
|
|||||||
@ -58,11 +58,7 @@
|
|||||||
<!-- <a-divider /> -->
|
<!-- <a-divider /> -->
|
||||||
|
|
||||||
<div class="config-segment" v-if="!isEmptyConfig">
|
<div class="config-segment" v-if="!isEmptyConfig">
|
||||||
<a-segmented
|
<a-segmented v-model:value="currentSegment" :options="segmentOptions" block />
|
||||||
v-model:value="currentSegment"
|
|
||||||
:options="segmentOptions"
|
|
||||||
block
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@ -571,15 +567,15 @@ const hasOtherConfigs = computed(() => {
|
|||||||
const segmentConfigKeys = computed(() => {
|
const segmentConfigKeys = computed(() => {
|
||||||
const keys = Object.keys(configurableItems.value)
|
const keys = Object.keys(configurableItems.value)
|
||||||
return {
|
return {
|
||||||
model: keys.filter(key => {
|
model: keys.filter((key) => {
|
||||||
const meta = configurableItems.value[key]?.template_metadata?.kind
|
const meta = configurableItems.value[key]?.template_metadata?.kind
|
||||||
return meta === 'llm' || meta === 'prompt'
|
return meta === 'llm' || meta === 'prompt'
|
||||||
}),
|
}),
|
||||||
tools: keys.filter(key => {
|
tools: keys.filter((key) => {
|
||||||
const meta = configurableItems.value[key]?.template_metadata?.kind
|
const meta = configurableItems.value[key]?.template_metadata?.kind
|
||||||
return ['tools', 'knowledges', 'mcps', 'skills', 'subagents'].includes(meta)
|
return ['tools', 'knowledges', 'mcps', 'skills', 'subagents'].includes(meta)
|
||||||
}),
|
}),
|
||||||
other: keys.filter(key => {
|
other: keys.filter((key) => {
|
||||||
const meta = configurableItems.value[key]?.template_metadata?.kind
|
const meta = configurableItems.value[key]?.template_metadata?.kind
|
||||||
return !['llm', 'prompt', 'tools', 'knowledges', 'mcps', 'skills', 'subagents'].includes(meta)
|
return !['llm', 'prompt', 'tools', 'knowledges', 'mcps', 'skills', 'subagents'].includes(meta)
|
||||||
})
|
})
|
||||||
@ -590,7 +586,7 @@ const filteredConfigurableItems = computed(() => {
|
|||||||
if (isEmptyConfig.value) return {}
|
if (isEmptyConfig.value) return {}
|
||||||
const keys = segmentConfigKeys.value[currentSegment.value] || []
|
const keys = segmentConfigKeys.value[currentSegment.value] || []
|
||||||
const filtered = {}
|
const filtered = {}
|
||||||
keys.forEach(key => {
|
keys.forEach((key) => {
|
||||||
filtered[key] = configurableItems.value[key]
|
filtered[key] = configurableItems.value[key]
|
||||||
})
|
})
|
||||||
return filtered
|
return filtered
|
||||||
|
|||||||
@ -136,7 +136,9 @@
|
|||||||
<pre v-if="Array.isArray(currentFile?.content)">{{
|
<pre v-if="Array.isArray(currentFile?.content)">{{
|
||||||
formatContent(currentFile.content)
|
formatContent(currentFile.content)
|
||||||
}}</pre>
|
}}</pre>
|
||||||
<pre v-else-if="typeof currentFile?.content === 'string'" class="file-content-pre">{{ currentFile.content }}</pre>
|
<pre v-else-if="typeof currentFile?.content === 'string'" class="file-content-pre">{{
|
||||||
|
currentFile.content
|
||||||
|
}}</pre>
|
||||||
<pre v-else>{{ JSON.stringify(currentFile, null, 2) }}</pre>
|
<pre v-else>{{ JSON.stringify(currentFile, null, 2) }}</pre>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@ -392,7 +394,12 @@ const refreshFileSystem = async () => {
|
|||||||
loadingFiles.value = true
|
loadingFiles.value = true
|
||||||
filesystemError.value = ''
|
filesystemError.value = ''
|
||||||
try {
|
try {
|
||||||
const res = await getViewerFileSystemTree(props.threadId, '/', props.agentId, props.agentConfigId)
|
const res = await getViewerFileSystemTree(
|
||||||
|
props.threadId,
|
||||||
|
'/',
|
||||||
|
props.agentId,
|
||||||
|
props.agentConfigId
|
||||||
|
)
|
||||||
if (res && res.entries) {
|
if (res && res.entries) {
|
||||||
dynamicTreeData.value = sortEntries(res.entries).map((entry) => createTreeNode(entry))
|
dynamicTreeData.value = sortEntries(res.entries).map((entry) => createTreeNode(entry))
|
||||||
expandedKeys.value = []
|
expandedKeys.value = []
|
||||||
@ -413,9 +420,7 @@ onMounted(() => {
|
|||||||
refreshFileSystem()
|
refreshFileSystem()
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch([() => props.threadId, () => props.agentId, () => props.agentConfigId], ([threadId]) => {
|
||||||
[() => props.threadId, () => props.agentId, () => props.agentConfigId],
|
|
||||||
([threadId]) => {
|
|
||||||
if (threadId) {
|
if (threadId) {
|
||||||
refreshFileSystem()
|
refreshFileSystem()
|
||||||
} else {
|
} else {
|
||||||
@ -424,8 +429,7 @@ watch(
|
|||||||
selectedKeys.value = []
|
selectedKeys.value = []
|
||||||
filesystemError.value = ''
|
filesystemError.value = ''
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
)
|
|
||||||
|
|
||||||
const fileTreeData = computed(() => dynamicTreeData.value)
|
const fileTreeData = computed(() => dynamicTreeData.value)
|
||||||
|
|
||||||
|
|||||||
@ -298,7 +298,6 @@
|
|||||||
</a-spin>
|
</a-spin>
|
||||||
</div>
|
</div>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
|
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,12 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="model-providers-section">
|
<div class="model-providers-section">
|
||||||
|
|
||||||
<div class="header-section">
|
<div class="header-section">
|
||||||
<div class="header-content">
|
<div class="header-content">
|
||||||
<div class="section-title">自定义供应商</div>
|
<div class="section-title">自定义供应商</div>
|
||||||
<p class="section-description">
|
<p class="section-description">添加自定义的LLM供应商,支持OpenAI兼容的API格式。</p>
|
||||||
添加自定义的LLM供应商,支持OpenAI兼容的API格式。
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<a-button type="primary" @click="openAddCustomProviderModal" class="add-btn lucide-icon-btn">
|
<a-button type="primary" @click="openAddCustomProviderModal" class="add-btn lucide-icon-btn">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
@ -103,7 +100,9 @@
|
|||||||
<span class="stats-item unavailable"> {{ notModelKeys.length }} 未配置 </span>
|
<span class="stats-item unavailable"> {{ notModelKeys.length }} 未配置 </span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="section-description">请在 <code>.env</code> 文件中配置对应的 APIKEY,并重新启动服务</p>
|
<p class="section-description">
|
||||||
|
请在 <code>.env</code> 文件中配置对应的 APIKEY,并重新启动服务
|
||||||
|
</p>
|
||||||
|
|
||||||
<!-- 已配置的供应商 -->
|
<!-- 已配置的供应商 -->
|
||||||
<div
|
<div
|
||||||
@ -174,9 +173,7 @@
|
|||||||
:width="800"
|
:width="800"
|
||||||
>
|
>
|
||||||
<div v-if="providerConfig.loading" class="modal-loading-container">
|
<div v-if="providerConfig.loading" class="modal-loading-container">
|
||||||
<a-spin
|
<a-spin :indicator="h(LoaderCircle, { size: 32, color: 'var(--main-color)' })" />
|
||||||
:indicator="h(LoaderCircle, { size: 32, color: 'var(--main-color)' })"
|
|
||||||
/>
|
|
||||||
<div class="loading-text">正在获取模型列表...</div>
|
<div class="loading-text">正在获取模型列表...</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="modal-config-content">
|
<div v-else class="modal-config-content">
|
||||||
@ -822,7 +819,6 @@ const testCustomProvider = async (providerId, modelName) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
||||||
.custom-providers-section {
|
.custom-providers-section {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
.custom-provider-card {
|
.custom-provider-card {
|
||||||
@ -918,7 +914,6 @@ const testCustomProvider = async (providerId, modelName) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.builtin-providers-section {
|
.builtin-providers-section {
|
||||||
|
|
||||||
.section-header {
|
.section-header {
|
||||||
.providers-stats {
|
.providers-stats {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@ -64,12 +64,7 @@
|
|||||||
<a-button v-else-if="showButton" type="primary" @click="goToLogin"> 登录 </a-button>
|
<a-button v-else-if="showButton" type="primary" @click="goToLogin"> 登录 </a-button>
|
||||||
|
|
||||||
<!-- 个人资料弹窗 -->
|
<!-- 个人资料弹窗 -->
|
||||||
<a-modal
|
<a-modal v-model:open="profileModalVisible" :footer="null" width="420px" class="profile-modal">
|
||||||
v-model:open="profileModalVisible"
|
|
||||||
:footer="null"
|
|
||||||
width="420px"
|
|
||||||
class="profile-modal"
|
|
||||||
>
|
|
||||||
<div class="profile-content">
|
<div class="profile-content">
|
||||||
<!-- 头像区域 -->
|
<!-- 头像区域 -->
|
||||||
<div class="avatar-section">
|
<div class="avatar-section">
|
||||||
@ -92,7 +87,12 @@
|
|||||||
@change="handleAvatarChange"
|
@change="handleAvatarChange"
|
||||||
accept="image/*"
|
accept="image/*"
|
||||||
>
|
>
|
||||||
<a-button type="primary" class="lucide-icon-btn" size="small" :loading="avatarUploading">
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
class="lucide-icon-btn"
|
||||||
|
size="small"
|
||||||
|
:loading="avatarUploading"
|
||||||
|
>
|
||||||
<template #icon><Upload size="14" /></template>
|
<template #icon><Upload size="14" /></template>
|
||||||
{{ userStore.avatar ? '更换头像' : '上传头像' }}
|
{{ userStore.avatar ? '更换头像' : '上传头像' }}
|
||||||
</a-button>
|
</a-button>
|
||||||
@ -143,7 +143,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<div class="info-label">角色</div>
|
<div class="info-label">角色</div>
|
||||||
<div class="info-value" :style="{'color': getRoleColor(userStore.userRole) }">
|
<div class="info-value" :style="{ color: getRoleColor(userStore.userRole) }">
|
||||||
{{ userRoleText }}
|
{{ userRoleText }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,7 +4,9 @@
|
|||||||
<div class="header-section">
|
<div class="header-section">
|
||||||
<div class="header-content">
|
<div class="header-content">
|
||||||
<div class="section-title">用户管理</div>
|
<div class="section-title">用户管理</div>
|
||||||
<p class="section-description">管理系统用户,请谨慎操作。删除用户后该用户将无法登录系统。</p>
|
<p class="section-description">
|
||||||
|
管理系统用户,请谨慎操作。删除用户后该用户将无法登录系统。
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<a-button type="primary" @click="showAddUserModal" class="add-btn lucide-icon-btn">
|
<a-button type="primary" @click="showAddUserModal" class="add-btn lucide-icon-btn">
|
||||||
<template #icon><Plus :size="16" /></template>
|
<template #icon><Plus :size="16" /></template>
|
||||||
|
|||||||
@ -155,9 +155,7 @@ const initToolsChart = () => {
|
|||||||
|
|
||||||
toolsChart = echarts.init(toolsChartRef.value)
|
toolsChart = echarts.init(toolsChartRef.value)
|
||||||
|
|
||||||
const data = [...props.toolStats.most_used_tools]
|
const data = [...props.toolStats.most_used_tools].sort((a, b) => a.count - b.count).slice(0, 10) // 只显示前10个,升序排列让最高的在最上面
|
||||||
.sort((a, b) => a.count - b.count)
|
|
||||||
.slice(0, 10) // 只显示前10个,升序排列让最高的在最上面
|
|
||||||
|
|
||||||
const option = {
|
const option = {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
|
|||||||
@ -62,7 +62,9 @@
|
|||||||
</p>
|
</p>
|
||||||
<h1 class="title reveal-up delay-1">{{ infoStore.branding.title }}</h1>
|
<h1 class="title reveal-up delay-1">{{ infoStore.branding.title }}</h1>
|
||||||
<Transition name="subtitle-switch" mode="out-in">
|
<Transition name="subtitle-switch" mode="out-in">
|
||||||
<p v-if="currentSubtitle" class="subtitle" :key="currentSubtitle">{{ currentSubtitle }}</p>
|
<p v-if="currentSubtitle" class="subtitle" :key="currentSubtitle">
|
||||||
|
{{ currentSubtitle }}
|
||||||
|
</p>
|
||||||
</Transition>
|
</Transition>
|
||||||
<!-- <p class="description">{{ infoStore.branding.description }}</p> -->
|
<!-- <p class="description">{{ infoStore.branding.description }}</p> -->
|
||||||
<div class="hero-actions">
|
<div class="hero-actions">
|
||||||
@ -281,9 +283,7 @@ const getHeroBadgeText = (starsCount = null) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const starValue =
|
const starValue =
|
||||||
typeof starFeature === 'string'
|
typeof starFeature === 'string' ? '' : (starFeature?.value || '').toString().trim()
|
||||||
? ''
|
|
||||||
: (starFeature?.value || '').toString().trim()
|
|
||||||
|
|
||||||
return starValue ? `已获得 ${starValue} GitHub Stars` : '已获得 GitHub Stars'
|
return starValue ? `已获得 ${starValue} GitHub Stars` : '已获得 GitHub Stars'
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user