fix: stabilize admin ui and agent chat model fallback
This commit is contained in:
@@ -75,7 +75,8 @@ class AgentService:
|
|||||||
流式事件
|
流式事件
|
||||||
"""
|
"""
|
||||||
# 检查模型是否支持 Function Calling
|
# 检查模型是否支持 Function Calling
|
||||||
supports_function_call = await self._check_model_supports_function_call(agent.model_id)
|
model_id = await self._resolve_agent_model_id(agent)
|
||||||
|
supports_function_call = await self._check_model_supports_function_call(model_id)
|
||||||
|
|
||||||
if supports_function_call:
|
if supports_function_call:
|
||||||
async for event in self._chat_autonomous_function_calling(agent, conversation, user_message, attachments):
|
async for event in self._chat_autonomous_function_calling(agent, conversation, user_message, attachments):
|
||||||
@@ -94,6 +95,43 @@ class AgentService:
|
|||||||
)
|
)
|
||||||
model = result.scalar_one_or_none()
|
model = result.scalar_one_or_none()
|
||||||
return model.supports_function_call if model else False
|
return model.supports_function_call if model else False
|
||||||
|
|
||||||
|
async def _resolve_agent_model_id(self, agent: Agent) -> Optional[str]:
|
||||||
|
if not self._db:
|
||||||
|
return str(agent.model_id) if agent.model_id else None
|
||||||
|
|
||||||
|
if agent.model_id:
|
||||||
|
result = await self._db.execute(
|
||||||
|
select(LLMModel).where(
|
||||||
|
LLMModel.id == agent.model_id,
|
||||||
|
LLMModel.is_deleted == False,
|
||||||
|
LLMModel.is_active == True,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if result.scalar_one_or_none():
|
||||||
|
return str(agent.model_id)
|
||||||
|
|
||||||
|
result = await self._db.execute(
|
||||||
|
select(LLMModel)
|
||||||
|
.where(
|
||||||
|
LLMModel.is_deleted == False,
|
||||||
|
LLMModel.is_active == True,
|
||||||
|
LLMModel.model_type == "chat",
|
||||||
|
)
|
||||||
|
.order_by(LLMModel.sort.desc(), LLMModel.sys_create_datetime.desc())
|
||||||
|
)
|
||||||
|
model = result.scalars().first()
|
||||||
|
if not model:
|
||||||
|
return None
|
||||||
|
|
||||||
|
agent.model_id = model.id
|
||||||
|
await self._db.flush()
|
||||||
|
logger.info(
|
||||||
|
"Agent %s has no active model, fallback to default chat model %s",
|
||||||
|
getattr(agent, "code", agent.id),
|
||||||
|
model.id,
|
||||||
|
)
|
||||||
|
return str(model.id)
|
||||||
|
|
||||||
async def _chat_autonomous_function_calling(
|
async def _chat_autonomous_function_calling(
|
||||||
self,
|
self,
|
||||||
@@ -214,6 +252,9 @@ class AgentService:
|
|||||||
|
|
||||||
# 检查是否启用流式输出
|
# 检查是否启用流式输出
|
||||||
enable_streaming = getattr(agent, 'enable_streaming', True)
|
enable_streaming = getattr(agent, 'enable_streaming', True)
|
||||||
|
model_id = await self._resolve_agent_model_id(agent)
|
||||||
|
if not model_id:
|
||||||
|
raise ValueError('未找到可用的 chat 模型,请先在模型配置中启用一个模型')
|
||||||
|
|
||||||
total_tokens = 0
|
total_tokens = 0
|
||||||
final_answer = ''
|
final_answer = ''
|
||||||
@@ -222,7 +263,7 @@ class AgentService:
|
|||||||
# 流式输出
|
# 流式输出
|
||||||
accumulated_content = ''
|
accumulated_content = ''
|
||||||
async for chunk in self.llm_service.chat_stream(
|
async for chunk in self.llm_service.chat_stream(
|
||||||
model_id=str(agent.model_id),
|
model_id=model_id,
|
||||||
messages=messages,
|
messages=messages,
|
||||||
temperature=agent.temperature or 0.7,
|
temperature=agent.temperature or 0.7,
|
||||||
max_tokens=agent.max_tokens or 2048,
|
max_tokens=agent.max_tokens or 2048,
|
||||||
@@ -241,7 +282,7 @@ class AgentService:
|
|||||||
else:
|
else:
|
||||||
# 非流式输出
|
# 非流式输出
|
||||||
response = await self.llm_service.chat_async(
|
response = await self.llm_service.chat_async(
|
||||||
model_id=str(agent.model_id),
|
model_id=model_id,
|
||||||
messages=messages,
|
messages=messages,
|
||||||
temperature=agent.temperature or 0.7,
|
temperature=agent.temperature or 0.7,
|
||||||
max_tokens=agent.max_tokens or 2048,
|
max_tokens=agent.max_tokens or 2048,
|
||||||
|
|||||||
@@ -194,21 +194,24 @@ function initComponentAdapter() {
|
|||||||
),
|
),
|
||||||
Checkbox: ElCheckbox,
|
Checkbox: ElCheckbox,
|
||||||
CheckboxGroup: (props, { attrs, slots }) => {
|
CheckboxGroup: (props, { attrs, slots }) => {
|
||||||
|
const mergedProps = { ...props, ...attrs } as Recordable<any>;
|
||||||
let defaultSlot;
|
let defaultSlot;
|
||||||
if (Reflect.has(slots, 'default')) {
|
if (Reflect.has(slots, 'default')) {
|
||||||
defaultSlot = slots.default;
|
defaultSlot = slots.default;
|
||||||
} else {
|
} else {
|
||||||
const { options, isButton } = attrs;
|
const { options, isButton } = mergedProps;
|
||||||
if (Array.isArray(options)) {
|
if (Array.isArray(options)) {
|
||||||
defaultSlot = () =>
|
defaultSlot = () =>
|
||||||
options.map((option) =>
|
options.map((option) =>
|
||||||
h(isButton ? ElCheckboxButton : ElCheckbox, option),
|
h(isButton ? ElCheckboxButton : ElCheckbox, option, () =>
|
||||||
|
option.label ?? option.value,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return h(
|
return h(
|
||||||
ElCheckboxGroup,
|
ElCheckboxGroup,
|
||||||
{ ...props, ...attrs },
|
mergedProps,
|
||||||
{ ...slots, default: defaultSlot },
|
{ ...slots, default: defaultSlot },
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -232,21 +235,24 @@ function initComponentAdapter() {
|
|||||||
}),
|
}),
|
||||||
InputNumber: withDefaultPlaceholder(ElInputNumber, 'input'),
|
InputNumber: withDefaultPlaceholder(ElInputNumber, 'input'),
|
||||||
RadioGroup: (props, { attrs, slots }) => {
|
RadioGroup: (props, { attrs, slots }) => {
|
||||||
|
const mergedProps = { ...props, ...attrs } as Recordable<any>;
|
||||||
let defaultSlot;
|
let defaultSlot;
|
||||||
if (Reflect.has(slots, 'default')) {
|
if (Reflect.has(slots, 'default')) {
|
||||||
defaultSlot = slots.default;
|
defaultSlot = slots.default;
|
||||||
} else {
|
} else {
|
||||||
const { options } = attrs;
|
const { options, isButton } = mergedProps;
|
||||||
if (Array.isArray(options)) {
|
if (Array.isArray(options)) {
|
||||||
defaultSlot = () =>
|
defaultSlot = () =>
|
||||||
options.map((option) =>
|
options.map((option) =>
|
||||||
h(attrs.isButton ? ElRadioButton : ElRadio, option),
|
h(isButton ? ElRadioButton : ElRadio, option, () =>
|
||||||
|
option.label ?? option.value,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return h(
|
return h(
|
||||||
ElRadioGroup,
|
ElRadioGroup,
|
||||||
{ ...props, ...attrs },
|
mergedProps,
|
||||||
{ ...slots, default: defaultSlot },
|
{ ...slots, default: defaultSlot },
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,6 +16,52 @@ import { initMessageAdapter } from './adapter/message';
|
|||||||
import { setupElementPlus } from './plugins/element-plus';
|
import { setupElementPlus } from './plugins/element-plus';
|
||||||
import { router } from './router';
|
import { router } from './router';
|
||||||
|
|
||||||
|
import 'element-plus/theme-chalk/base.css';
|
||||||
|
import 'element-plus/theme-chalk/dark/css-vars.css';
|
||||||
|
import 'element-plus/theme-chalk/el-overlay.css';
|
||||||
|
import 'element-plus/theme-chalk/el-popper.css';
|
||||||
|
import 'element-plus/theme-chalk/el-icon.css';
|
||||||
|
import 'element-plus/theme-chalk/el-button.css';
|
||||||
|
import 'element-plus/theme-chalk/el-button-group.css';
|
||||||
|
import 'element-plus/theme-chalk/el-checkbox.css';
|
||||||
|
import 'element-plus/theme-chalk/el-checkbox-button.css';
|
||||||
|
import 'element-plus/theme-chalk/el-checkbox-group.css';
|
||||||
|
import 'element-plus/theme-chalk/el-radio.css';
|
||||||
|
import 'element-plus/theme-chalk/el-radio-button.css';
|
||||||
|
import 'element-plus/theme-chalk/el-radio-group.css';
|
||||||
|
import 'element-plus/theme-chalk/el-input.css';
|
||||||
|
import 'element-plus/theme-chalk/el-input-number.css';
|
||||||
|
import 'element-plus/theme-chalk/el-select.css';
|
||||||
|
import 'element-plus/theme-chalk/el-select-v2.css';
|
||||||
|
import 'element-plus/theme-chalk/el-select-dropdown.css';
|
||||||
|
import 'element-plus/theme-chalk/el-select-dropdown-v2.css';
|
||||||
|
import 'element-plus/theme-chalk/el-option.css';
|
||||||
|
import 'element-plus/theme-chalk/el-option-group.css';
|
||||||
|
import 'element-plus/theme-chalk/el-tree.css';
|
||||||
|
import 'element-plus/theme-chalk/el-tree-select.css';
|
||||||
|
import 'element-plus/theme-chalk/el-form.css';
|
||||||
|
import 'element-plus/theme-chalk/el-form-item.css';
|
||||||
|
import 'element-plus/theme-chalk/el-table.css';
|
||||||
|
import 'element-plus/theme-chalk/el-table-column.css';
|
||||||
|
import 'element-plus/theme-chalk/el-card.css';
|
||||||
|
import 'element-plus/theme-chalk/el-dialog.css';
|
||||||
|
import 'element-plus/theme-chalk/el-message.css';
|
||||||
|
import 'element-plus/theme-chalk/el-message-box.css';
|
||||||
|
import 'element-plus/theme-chalk/el-notification.css';
|
||||||
|
import 'element-plus/theme-chalk/el-popover.css';
|
||||||
|
import 'element-plus/theme-chalk/el-tooltip.css';
|
||||||
|
import 'element-plus/theme-chalk/el-scrollbar.css';
|
||||||
|
import 'element-plus/theme-chalk/el-empty.css';
|
||||||
|
import 'element-plus/theme-chalk/el-tag.css';
|
||||||
|
import 'element-plus/theme-chalk/el-pagination.css';
|
||||||
|
import 'element-plus/theme-chalk/el-loading.css';
|
||||||
|
import 'element-plus/theme-chalk/el-divider.css';
|
||||||
|
import 'element-plus/theme-chalk/el-skeleton.css';
|
||||||
|
import 'element-plus/theme-chalk/el-skeleton-item.css';
|
||||||
|
import 'element-plus/theme-chalk/el-cascader.css';
|
||||||
|
import 'element-plus/theme-chalk/el-cascader-panel.css';
|
||||||
|
import 'element-plus/theme-chalk/el-date-picker.css';
|
||||||
|
|
||||||
async function bootstrap(namespace: string) {
|
async function bootstrap(namespace: string) {
|
||||||
// // 设置弹窗的默认配置
|
// // 设置弹窗的默认配置
|
||||||
// setDefaultModalProps({
|
// setDefaultModalProps({
|
||||||
|
|||||||
@@ -2,6 +2,70 @@
|
|||||||
--el-card-border-radius: var(--radius) !important;
|
--el-card-border-radius: var(--radius) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html.dark {
|
||||||
|
color-scheme: dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .el-card,
|
||||||
|
html.dark .el-dialog,
|
||||||
|
html.dark .el-message-box,
|
||||||
|
html.dark .el-popover,
|
||||||
|
html.dark .el-table,
|
||||||
|
html.dark .el-table__expanded-cell {
|
||||||
|
color: hsl(var(--foreground));
|
||||||
|
background-color: hsl(var(--card));
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .el-table {
|
||||||
|
--el-table-bg-color: hsl(var(--card));
|
||||||
|
--el-table-tr-bg-color: hsl(var(--card));
|
||||||
|
--el-table-header-bg-color: hsl(var(--background-deep));
|
||||||
|
--el-table-row-hover-bg-color: hsl(var(--accent));
|
||||||
|
--el-table-current-row-bg-color: hsl(var(--accent));
|
||||||
|
--el-table-border-color: hsl(var(--border));
|
||||||
|
--el-table-text-color: hsl(var(--foreground));
|
||||||
|
--el-table-header-text-color: hsl(var(--muted-foreground));
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .el-table__body tr,
|
||||||
|
html.dark .el-table__body td.el-table__cell,
|
||||||
|
html.dark .el-table__fixed-right,
|
||||||
|
html.dark .el-table__fixed-left {
|
||||||
|
color: hsl(var(--foreground));
|
||||||
|
background-color: hsl(var(--card));
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .el-table--striped .el-table__body tr.el-table__row--striped td.el-table__cell {
|
||||||
|
background-color: hsl(var(--accent) / 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .el-table__body tr.hover-row > td.el-table__cell,
|
||||||
|
html.dark .el-table__body tr:hover > td.el-table__cell {
|
||||||
|
background-color: hsl(var(--accent));
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .el-input__wrapper,
|
||||||
|
html.dark .el-select__wrapper,
|
||||||
|
html.dark .el-textarea__inner,
|
||||||
|
html.dark .el-input-number .el-input__wrapper {
|
||||||
|
color: hsl(var(--foreground));
|
||||||
|
background-color: hsl(var(--background));
|
||||||
|
box-shadow: 0 0 0 1px hsl(var(--border)) inset;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .el-input__inner,
|
||||||
|
html.dark .el-select__placeholder,
|
||||||
|
html.dark .el-textarea__inner {
|
||||||
|
color: hsl(var(--foreground));
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table .hidden-columns {
|
||||||
|
position: absolute !important;
|
||||||
|
z-index: -1 !important;
|
||||||
|
display: none !important;
|
||||||
|
visibility: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
.el-dialog {
|
.el-dialog {
|
||||||
--el-dialog-border-radius: var(--radius) !important;
|
--el-dialog-border-radius: var(--radius) !important;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user