diff --git a/backend-fastapi/ai_platform/services/agent_service.py b/backend-fastapi/ai_platform/services/agent_service.py index e58b4ca..dd3fa2e 100644 --- a/backend-fastapi/ai_platform/services/agent_service.py +++ b/backend-fastapi/ai_platform/services/agent_service.py @@ -75,7 +75,8 @@ class AgentService: 流式事件 """ # 检查模型是否支持 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: 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() 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( self, @@ -214,6 +252,9 @@ class AgentService: # 检查是否启用流式输出 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 final_answer = '' @@ -222,7 +263,7 @@ class AgentService: # 流式输出 accumulated_content = '' async for chunk in self.llm_service.chat_stream( - model_id=str(agent.model_id), + model_id=model_id, messages=messages, temperature=agent.temperature or 0.7, max_tokens=agent.max_tokens or 2048, @@ -241,7 +282,7 @@ class AgentService: else: # 非流式输出 response = await self.llm_service.chat_async( - model_id=str(agent.model_id), + model_id=model_id, messages=messages, temperature=agent.temperature or 0.7, max_tokens=agent.max_tokens or 2048, diff --git a/web/apps/web-ele/src/adapter/component/index.ts b/web/apps/web-ele/src/adapter/component/index.ts index 09f8778..79b064f 100644 --- a/web/apps/web-ele/src/adapter/component/index.ts +++ b/web/apps/web-ele/src/adapter/component/index.ts @@ -194,21 +194,24 @@ function initComponentAdapter() { ), Checkbox: ElCheckbox, CheckboxGroup: (props, { attrs, slots }) => { + const mergedProps = { ...props, ...attrs } as Recordable; let defaultSlot; if (Reflect.has(slots, 'default')) { defaultSlot = slots.default; } else { - const { options, isButton } = attrs; + const { options, isButton } = mergedProps; if (Array.isArray(options)) { defaultSlot = () => options.map((option) => - h(isButton ? ElCheckboxButton : ElCheckbox, option), + h(isButton ? ElCheckboxButton : ElCheckbox, option, () => + option.label ?? option.value, + ), ); } } return h( ElCheckboxGroup, - { ...props, ...attrs }, + mergedProps, { ...slots, default: defaultSlot }, ); }, @@ -232,21 +235,24 @@ function initComponentAdapter() { }), InputNumber: withDefaultPlaceholder(ElInputNumber, 'input'), RadioGroup: (props, { attrs, slots }) => { + const mergedProps = { ...props, ...attrs } as Recordable; let defaultSlot; if (Reflect.has(slots, 'default')) { defaultSlot = slots.default; } else { - const { options } = attrs; + const { options, isButton } = mergedProps; if (Array.isArray(options)) { defaultSlot = () => options.map((option) => - h(attrs.isButton ? ElRadioButton : ElRadio, option), + h(isButton ? ElRadioButton : ElRadio, option, () => + option.label ?? option.value, + ), ); } } return h( ElRadioGroup, - { ...props, ...attrs }, + mergedProps, { ...slots, default: defaultSlot }, ); }, diff --git a/web/apps/web-ele/src/bootstrap.ts b/web/apps/web-ele/src/bootstrap.ts index 599df02..688a49a 100644 --- a/web/apps/web-ele/src/bootstrap.ts +++ b/web/apps/web-ele/src/bootstrap.ts @@ -16,6 +16,52 @@ import { initMessageAdapter } from './adapter/message'; import { setupElementPlus } from './plugins/element-plus'; 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) { // // 设置弹窗的默认配置 // setDefaultModalProps({ diff --git a/web/packages/styles/src/ele/index.css b/web/packages/styles/src/ele/index.css index ab55f96..275ec34 100644 --- a/web/packages/styles/src/ele/index.css +++ b/web/packages/styles/src/ele/index.css @@ -2,6 +2,70 @@ --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-border-radius: var(--radius) !important; }