style: code format

This commit is contained in:
Wenjie Zhang 2026-06-03 18:55:34 +08:00
parent 0fdc0acf91
commit 3b35493db3
21 changed files with 126 additions and 54 deletions

View File

@ -15,9 +15,9 @@ from langgraph.types import Command
from yuxi import config as sys_config
from yuxi.agents.context import BaseContext, resolve_agent_resource_options
from yuxi.utils.subagent_thread_utils import make_child_thread_id
from yuxi.storage.postgres.manager import pg_manager
from yuxi.utils import logger
from yuxi.utils.subagent_thread_utils import make_child_thread_id
def _json_safe(value: Any) -> Any:
@ -79,9 +79,7 @@ def _subagent_route_for_namespace(
return None
async def _collect_subagent_routes(
run, parent_thread_id: str, routes: dict[tuple[str, ...], dict[str, str]]
) -> None:
async def _collect_subagent_routes(run, parent_thread_id: str, routes: dict[tuple[str, ...], dict[str, str]]) -> None:
subagents = getattr(run, "subagents", None)
if subagents is None:
return

View File

@ -10,7 +10,7 @@ class ChatBotContext(BaseContext):
metadata={
"name": "子智能体",
"options": [],
"description": "可选子智能体列表,只有被选中的子智能体会作为 task 工具候选。为空表示启用当前用户可见的全部子智能体。",
"description": "可选子智能体列表,为空表示启用当前用户可见的全部子智能体。",
"type": "list",
"kind": "subagents",
},

View File

@ -15,7 +15,6 @@ from yuxi.agents.middlewares.knowledge_base import KnowledgeBaseMiddleware
from yuxi.agents.middlewares.skills import SkillsMiddleware
from yuxi.agents.toolkits.service import resolve_configured_runtime_tools
_SUBAGENT_DISABLED_TOOLS = frozenset({"present_artifacts", "ask_user_question"})

View File

@ -15,13 +15,13 @@ from langgraph.prebuilt.tool_node import ToolRuntime
from langgraph.types import Command
from yuxi.agents.context import build_agent_input_context
from yuxi.utils.subagent_thread_utils import make_child_thread_id
from yuxi.repositories.agent_repository import SUB_AGENT_BACKEND_ID, AgentRepository
from yuxi.repositories.agent_run_repository import AgentRunRepository
from yuxi.repositories.user_repository import UserRepository
from yuxi.storage.postgres.manager import pg_manager
from yuxi.storage.postgres.models_business import Agent
from yuxi.utils.datetime_utils import utc_isoformat
from yuxi.utils.subagent_thread_utils import make_child_thread_id
_CHILD_STATE_INHERIT_KEYS: frozenset[str] = frozenset()
_TERMINAL_RUN_STATUSES = {"completed", "failed", "cancelled", "interrupted"}
@ -195,11 +195,7 @@ def _state_for_child(
skills_thread_id: str,
continuing: bool = False,
) -> dict[str, Any]:
state = (
{}
if continuing
else {key: runtime.state[key] for key in _CHILD_STATE_INHERIT_KEYS if key in runtime.state}
)
state = {} if continuing else {key: runtime.state[key] for key in _CHILD_STATE_INHERIT_KEYS if key in runtime.state}
state.update(
{
"parent_thread_id": parent_thread_id,

View File

@ -20,18 +20,19 @@ DEFAULT_SHARE_CONFIG = {"access_level": "global", "department_ids": [], "user_ui
WEB_SEARCH_AGENT_SLUG = "web-search"
WEB_SEARCH_AGENT_NAME = "网页检索"
WEB_SEARCH_AGENT_DESCRIPTION = "围绕检索目标持续搜索网页,返回带引用来源的摘要资料。"
WEB_SEARCH_SYSTEM_PROMPT = """你是「网页检索」子智能体,由主智能体通过 task 工具调用,专注于面向目标的网页信息检索。
WEB_SEARCH_SYSTEM_PROMPT = """你是「网页检索」子智能体,专注于面向目标的网页信息检索。
你的职责围绕调用方给定的检索目标使用网页搜索工具持续检索直到收集到足以回答目标的信息
工作方式
1. 拆解目标确定需要检索的关键问题与检索词
2. 多轮调用网页搜索工具依据上一轮结果调整检索词补充遗漏角度交叉验证关键事实直到信息充分或确认无法获取更多有效信息
2. 多轮调用搜索工具依据上一轮结果调整检索词补充遗漏角度交叉验证关键事实直到信息充分或确认无法获取更多有效信息
3. 优先采信权威时效性强且彼此印证的来源对存在冲突的信息要说明分歧
输出要求
- 返回一份结构化的摘要资料按主题或要点组织
- 每条关键结论后使用 <cite source="$URL" type="url">$INDEX</cite> 标注引用来源$INDEX 1 开始递增不单独成行直接跟在结论后面
- 每条关键结论后使用 <cite source="$URL" type="url">$INDEX</cite> 标注引用来源$INDEX 1 开始递增
- 引用不单独成行直接跟在结论后面
- 在结尾汇总参考来源列表逐条列出标题与 URL
- 不要编造来源或链接无法验证的信息要明确标注"""
ACCESS_LEVELS = {"global", "department", "user"}

View File

@ -29,6 +29,7 @@ from yuxi.utils.question_utils import (
normalize_questions as _normalize_interrupt_questions,
)
def _build_state_files(attachments: list[dict]) -> dict:
"""将附件列表转换为 StateBackend 格式的 files 字典

View File

@ -2,7 +2,6 @@ from __future__ import annotations
import hashlib
_CHILD_THREAD_ID_PREFIX = "subagent_"
_CHILD_THREAD_ID_DIGEST_LENGTH = 64 - len(_CHILD_THREAD_ID_PREFIX)

View File

@ -45,7 +45,10 @@
<div class="chat-box">
<template v-for="row in conversationRows" :key="row.key">
<div v-if="row.type === 'conversation'" class="conv-box">
<template v-for="(displayItem, itemIndex) in row.displayItems" :key="displayItem.key">
<template
v-for="(displayItem, itemIndex) in row.displayItems"
:key="displayItem.key"
>
<AgentMessageComponent
v-if="displayItem.type === 'message'"
:message="displayItem.message"
@ -547,7 +550,9 @@ const getArtifactMetaLabel = (path) => {
const getSubagentRunName = (run) => {
const subagentType = run?.subagent_type ? String(run.subagent_type) : ''
return run?.subagent_name || currentSubagentOptionBySlug.value.get(subagentType)?.name || '子智能体'
return (
run?.subagent_name || currentSubagentOptionBySlug.value.get(subagentType)?.name || '子智能体'
)
}
const getSubagentAgent = (run) => {
@ -824,7 +829,9 @@ const currentThreadConfigNotice = computed(() => {
const shouldSuppressRefsForApproval = () =>
approvalState.showModal ||
Boolean(
approvalState.threadId && chatState.currentThreadId === approvalState.threadId && isProcessing.value
approvalState.threadId &&
chatState.currentThreadId === approvalState.threadId &&
isProcessing.value
)
// Refs
@ -911,7 +918,9 @@ const runningSubagentRunsFromStream = computed(() => {
return ongoingTaskCalls.value
.filter((call) => activeIds.has(call.id))
.map((call) => {
const option = call.subagentType ? currentSubagentOptionBySlug.value.get(call.subagentType) : null
const option = call.subagentType
? currentSubagentOptionBySlug.value.get(call.subagentType)
: null
return {
id: call.id,
subagent_type: call.subagentType,

View File

@ -569,7 +569,9 @@ const ensureArray = (key) => {
if (!config[key] || !Array.isArray(config[key])) {
return []
}
const validValues = new Set(getConfigOptions(configItem).map((option) => String(getOptionValue(option))))
const validValues = new Set(
getConfigOptions(configItem).map((option) => String(getOptionValue(option)))
)
if (validValues.size === 0) return config[key]
return config[key].filter((value) => validValues.has(String(value)))
}

View File

@ -34,10 +34,15 @@ const props = defineProps({
}
})
const conversations = computed(() => MessageProcessor.convertServerHistoryToMessages(props.messages))
const conversations = computed(() =>
MessageProcessor.convertServerHistoryToMessages(props.messages)
)
const getDisplayItems = (conv) =>
getConversationDisplayItems(conv, props.enrichToolCalls ? { enrichToolCalls: props.enrichToolCalls } : {})
getConversationDisplayItems(
conv,
props.enrichToolCalls ? { enrichToolCalls: props.enrichToolCalls } : {}
)
</script>
<style lang="less" scoped>

View File

@ -58,10 +58,10 @@ export const isHiddenToolCall = (toolCall) => HIDDEN_TOOL_CALL_IDS.includes(getT
export const isValidToolCall = (toolCall) => {
return Boolean(
toolCall &&
(toolCall.id || toolCall.name || toolCall.function?.name) &&
(toolCall.args !== undefined ||
toolCall.function?.arguments !== undefined ||
toolCall.tool_call_result !== undefined)
(toolCall.id || toolCall.name || toolCall.function?.name) &&
(toolCall.args !== undefined ||
toolCall.function?.arguments !== undefined ||
toolCall.tool_call_result !== undefined)
)
}
@ -76,15 +76,21 @@ export const parseToolCallArgs = (toolCall) => {
}
}
export const enrichTaskToolCall = (toolCall, { subagentRunById, subagentRunByThreadId, subagentOptionBySlug } = {}) => {
export const enrichTaskToolCall = (
toolCall,
{ subagentRunById, subagentRunByThreadId, subagentOptionBySlug } = {}
) => {
if (getToolCallId(toolCall) !== 'task') return toolCall
const args = parseToolCallArgs(toolCall)
const subagentRun =
(toolCall.id ? subagentRunById?.get?.(String(toolCall.id)) : null) ||
(args.thread_id ? subagentRunByThreadId?.get?.(String(args.thread_id)) : null)
const subagentOption = args.subagent_type ? subagentOptionBySlug?.get?.(String(args.subagent_type)) : null
const displayLabel = subagentRun?.subagent_name || subagentOption?.name || subagentRun?.subagent_type || undefined
const subagentOption = args.subagent_type
? subagentOptionBySlug?.get?.(String(args.subagent_type))
: null
const displayLabel =
subagentRun?.subagent_name || subagentOption?.name || subagentRun?.subagent_type || undefined
return {
...toolCall,
@ -105,6 +111,8 @@ export const normalizeToolCalls = (toolCalls, { includeHidden = false, mapToolCa
}
export const enrichTaskToolCalls = (toolCalls, options = {}) =>
normalizeToolCalls(toolCalls, { mapToolCall: (toolCall) => enrichTaskToolCall(toolCall, options) })
normalizeToolCalls(toolCalls, {
mapToolCall: (toolCall) => enrichTaskToolCall(toolCall, options)
})
export const getToolIcon = (toolId) => TOOL_ICON_MAP[toolId] || null

View File

@ -1,5 +1,9 @@
<template>
<BaseToolCall :tool-call="toolCall" :status="baseStatus" :force-show-result="Boolean(displayResult)">
<BaseToolCall
:tool-call="toolCall"
:status="baseStatus"
:force-show-result="Boolean(displayResult)"
>
<template #header>
<div class="sep-header">
<span class="note">{{ subagentDisplayName }}</span>
@ -67,9 +71,13 @@ const childThreadId = computed(
(getSubagentThreadIdByToolCall ? getSubagentThreadIdByToolCall(props.toolCall.id) : '') ||
''
)
const hasToolResult = computed(() => Boolean(props.toolCall.tool_call_result || props.toolCall.result))
const hasToolResult = computed(() =>
Boolean(props.toolCall.tool_call_result || props.toolCall.result)
)
// 线 steer
const isActiveRun = computed(() => Boolean(activeSubagentToolCallIds?.value?.has(String(props.toolCall.id))))
const isActiveRun = computed(() =>
Boolean(activeSubagentToolCallIds?.value?.has(String(props.toolCall.id)))
)
const runStatus = computed(() => {
if (props.toolCall.status === 'error') return 'failed'
// ongoing /
@ -114,7 +122,9 @@ const shortDescription = computed(() => {
const isRunning = computed(() => runStatus.value === 'running')
const truncate = (text, limit = 50) => {
const value = String(text || '').replace(/\s+/g, ' ').trim()
const value = String(text || '')
.replace(/\s+/g, ' ')
.trim()
if (!value) return ''
return value.length > limit ? value.slice(0, limit) + '...' : value
}
@ -122,7 +132,8 @@ const truncate = (text, limit = 50) => {
const formatToolCall = (toolCall) => {
const name = toolCall?.name || toolCall?.function?.name || 'tool'
const rawArgs = toolCall?.args ?? toolCall?.function?.arguments
const args = rawArgs && typeof rawArgs === 'object' ? JSON.stringify(rawArgs) : String(rawArgs ?? '')
const args =
rawArgs && typeof rawArgs === 'object' ? JSON.stringify(rawArgs) : String(rawArgs ?? '')
return `Call(${name}): ${args}`
}

View File

@ -826,7 +826,6 @@ onMounted(async () => {
height: 100%;
object-fit: cover;
}
}
.user-info-content {

View File

@ -141,7 +141,12 @@
{{ formatExtensionCardTitle(previewSkill.name) }}
</div>
<div class="skill-preview-meta">
<span>{{ sourceTypeLabel(previewSkill.sourceType || previewSkill.source_type) }} Skill</span>
<span
>{{
sourceTypeLabel(previewSkill.sourceType || previewSkill.source_type)
}}
Skill</span
>
<span v-if="previewSkill.enabled === false" class="skill-preview-disabled-tag">
已禁用
</span>
@ -525,7 +530,17 @@
import { computed, onMounted, reactive, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import { message, Modal } from 'ant-design-vue'
import { RefreshCw, Upload, Computer, BookMarked, History, Trash2, Check, Plus, Minus } from 'lucide-vue-next'
import {
RefreshCw,
Upload,
Computer,
BookMarked,
History,
Trash2,
Check,
Plus,
Minus
} from 'lucide-vue-next'
import { skillApi } from '@/apis/skill_api'
import ExtensionCardGrid from './ExtensionCardGrid.vue'
import InfoCard from '@/components/shared/InfoCard.vue'
@ -601,7 +616,10 @@ const filteredDeletableSkills = computed(() =>
)
)
const canDeletePreviewSkill = computed(
() => !!previewSkill.value && canManageSkill(previewSkill.value) && previewSkill.value.sourceType !== 'builtin'
() =>
!!previewSkill.value &&
canManageSkill(previewSkill.value) &&
previewSkill.value.sourceType !== 'builtin'
)
//

View File

@ -402,7 +402,9 @@ const isBuiltinInstalledSkill = computed(() => {
})
const canManageCurrentSkill = computed(() => currentSkill.value?.can_manage !== false)
const isReadOnlySkill = computed(() => isInstalledSkill.value && !canManageCurrentSkill.value)
const canEditSkillFiles = computed(() => canManageCurrentSkill.value && !isBuiltinInstalledSkill.value)
const canEditSkillFiles = computed(
() => canManageCurrentSkill.value && !isBuiltinInstalledSkill.value
)
const canEditSkillDependencies = computed(
() => canManageCurrentSkill.value && !isBuiltinInstalledSkill.value
)

View File

@ -617,7 +617,6 @@ defineExpose({
min-height: 0;
}
.agent-card-icon-image {
display: block;
width: 100%;

View File

@ -81,7 +81,10 @@
</div>
</div>
<div v-if="$slots.tags || (normalizedTags && normalizedTags.length > 0)" class="info-card-tags">
<div
v-if="$slots.tags || (normalizedTags && normalizedTags.length > 0)"
class="info-card-tags"
>
<slot name="tags">
<span
v-for="(tag, idx) in normalizedTags"

View File

@ -171,7 +171,12 @@ export function useAgentRunStream({
}
}
const finalizeRunStream = (threadId, runId, touchedThreadIds, { delay = 200, scroll = false } = {}) => {
const finalizeRunStream = (
threadId,
runId,
touchedThreadIds,
{ delay = 200, scroll = false } = {}
) => {
const ts = getThreadState(threadId)
if (!ts || ts.activeRunId !== runId) return
touchedThreadIds.forEach((id) => streamSmoother?.flushThread(id))
@ -276,7 +281,11 @@ export function useAgentRunStream({
})
touchedThreadIds.add(routeThreadId)
handleStreamChunk(
{ ...payload.chunk, run_id: payload.chunk.run_id || data.run_id || runId, thread_id: routeThreadId },
{
...payload.chunk,
run_id: payload.chunk.run_id || data.run_id || runId,
thread_id: routeThreadId
},
routeThreadId
)
}
@ -307,7 +316,10 @@ export function useAgentRunStream({
scheduleRunReconnect(threadId, runId)
}
} catch (e) {
console.warn('Run SSE closed before terminal event; reconnecting after status check failed:', e)
console.warn(
'Run SSE closed before terminal event; reconnecting after status check failed:',
e
)
scheduleRunReconnect(threadId, runId)
}
}

View File

@ -32,8 +32,16 @@ const run = () => {
}
{
assert.throws(() => generatePixelAvatar(''), /requires an id/, 'Empty ID should be treated as invalid data')
assert.throws(() => generatePixelAvatar(null), /requires an id/, 'Null ID should be treated as invalid data')
assert.throws(
() => generatePixelAvatar(''),
/requires an id/,
'Empty ID should be treated as invalid data'
)
assert.throws(
() => generatePixelAvatar(null),
/requires an id/,
'Null ID should be treated as invalid data'
)
console.log('T5 Missing ID fails: PASS')
}

View File

@ -7,17 +7,20 @@ const hasVisibleAssistantBody = (message) => {
const { content, reasoningContent } = MessageProcessor.parseAssistantMessageBody(message)
return Boolean(
content ||
reasoningContent ||
message.error_type ||
message.extra_metadata?.error_type ||
message.isStoppedByUser
reasoningContent ||
message.error_type ||
message.extra_metadata?.error_type ||
message.isStoppedByUser
)
}
const defaultEnrichToolCalls = (message) => enrichTaskToolCalls(message?.tool_calls)
// 将 AI 消息拆成“正文块”和“工具块”,再跨消息合并相邻工具块。
export const getConversationDisplayItems = (conv, { enrichToolCalls = defaultEnrichToolCalls } = {}) => {
export const getConversationDisplayItems = (
conv,
{ enrichToolCalls = defaultEnrichToolCalls } = {}
) => {
if (!Array.isArray(conv?.messages) || conv.messages.length === 0) return []
const items = []

View File

@ -419,7 +419,6 @@ export class MessageProcessor {
}
}
}
}
export default MessageProcessor