mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-09-22 16:00:51 +00:00
feat(ai-agents): add time zone config for openclaw (#12020)
This commit is contained in:
@@ -394,6 +394,47 @@ func (b *BaseApi) UpdateAgentBrowserConfig(c *gin.Context) {
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Get Agent Other config
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentOtherConfigReq true "request"
|
||||
// @Success 200 {object} dto.AgentOtherConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/other/get [post]
|
||||
func (b *BaseApi) GetAgentOtherConfig(c *gin.Context) {
|
||||
var req dto.AgentOtherConfigReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
data, err := agentService.GetOtherConfig(req)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Update Agent Other config
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentOtherConfigUpdateReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/other/update [post]
|
||||
func (b *BaseApi) UpdateAgentOtherConfig(c *gin.Context) {
|
||||
var req dto.AgentOtherConfigUpdateReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := agentService.UpdateOtherConfig(req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Approve Agent Feishu pairing code
|
||||
// @Accept json
|
||||
|
||||
@@ -238,3 +238,16 @@ type AgentBrowserConfig struct {
|
||||
NoSandbox bool `json:"noSandbox"`
|
||||
DefaultProfile string `json:"defaultProfile"`
|
||||
}
|
||||
|
||||
type AgentOtherConfigReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentOtherConfigUpdateReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
UserTimezone string `json:"userTimezone" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentOtherConfig struct {
|
||||
UserTimezone string `json:"userTimezone"`
|
||||
}
|
||||
|
||||
@@ -49,6 +49,8 @@ type IAgentService interface {
|
||||
UpdateDiscordConfig(req dto.AgentDiscordConfigUpdateReq) error
|
||||
GetBrowserConfig(req dto.AgentBrowserConfigReq) (*dto.AgentBrowserConfig, error)
|
||||
UpdateBrowserConfig(req dto.AgentBrowserConfigUpdateReq) error
|
||||
GetOtherConfig(req dto.AgentOtherConfigReq) (*dto.AgentOtherConfig, error)
|
||||
UpdateOtherConfig(req dto.AgentOtherConfigUpdateReq) error
|
||||
ApproveChannelPairing(req dto.AgentChannelPairingApproveReq) error
|
||||
ApproveFeishuPairing(req dto.AgentFeishuPairingApproveReq) error
|
||||
}
|
||||
@@ -60,6 +62,7 @@ func NewIAgentService() IAgentService {
|
||||
const (
|
||||
defaultBrowserExecutablePath = "/home/node/.cache/ms-playwright/chromium-1208/chrome-linux64/chrome"
|
||||
defaultBrowserProfile = "openclaw"
|
||||
defaultUserTimezone = "Asia/Shanghai"
|
||||
)
|
||||
|
||||
func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {
|
||||
@@ -767,6 +770,35 @@ func (a AgentService) UpdateBrowserConfig(req dto.AgentBrowserConfigUpdateReq) e
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a AgentService) GetOtherConfig(req dto.AgentOtherConfigReq) (*dto.AgentOtherConfig, error) {
|
||||
agent, _, err := a.loadAgentAndInstall(req.AgentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conf, err := readOpenclawConfig(agent.ConfigPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := extractOtherConfig(conf)
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (a AgentService) UpdateOtherConfig(req dto.AgentOtherConfigUpdateReq) error {
|
||||
agent, _, err := a.loadAgentAndInstall(req.AgentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conf, err := readOpenclawConfig(agent.ConfigPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
setOtherConfig(conf, dto.AgentOtherConfig{UserTimezone: strings.TrimSpace(req.UserTimezone)})
|
||||
if err := writeOpenclawConfigRaw(agent.ConfigPath, conf); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a AgentService) ApproveChannelPairing(req dto.AgentChannelPairingApproveReq) error {
|
||||
_, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||
if err != nil {
|
||||
@@ -1038,6 +1070,32 @@ func setBrowserConfig(conf map[string]interface{}, config dto.AgentBrowserConfig
|
||||
}
|
||||
}
|
||||
|
||||
func extractOtherConfig(conf map[string]interface{}) dto.AgentOtherConfig {
|
||||
result := dto.AgentOtherConfig{UserTimezone: resolveServerTimezone()}
|
||||
agents, ok := conf["agents"].(map[string]interface{})
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
defaults, ok := agents["defaults"].(map[string]interface{})
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
if timezone, ok := defaults["userTimezone"].(string); ok && strings.TrimSpace(timezone) != "" {
|
||||
result.UserTimezone = strings.TrimSpace(timezone)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func setOtherConfig(conf map[string]interface{}, config dto.AgentOtherConfig) {
|
||||
agents := ensureChildMap(conf, "agents")
|
||||
defaults := ensureChildMap(agents, "defaults")
|
||||
timezone := strings.TrimSpace(config.UserTimezone)
|
||||
if timezone == "" {
|
||||
timezone = resolveServerTimezone()
|
||||
}
|
||||
defaults["userTimezone"] = timezone
|
||||
}
|
||||
|
||||
func (a AgentService) syncAgentsByAccount(account *model.AgentAccount) error {
|
||||
agents, err := agentRepo.List(repo.WithByAccountID(account.ID))
|
||||
if err != nil {
|
||||
@@ -1333,7 +1391,8 @@ type agentsConfig struct {
|
||||
}
|
||||
|
||||
type agentDefaults struct {
|
||||
Model modelRef `json:"model"`
|
||||
UserTimezone string `json:"userTimezone,omitempty"`
|
||||
Model modelRef `json:"model"`
|
||||
}
|
||||
|
||||
type modelRef struct {
|
||||
@@ -1410,7 +1469,8 @@ func writeOpenclawConfig(confDir, provider, modelName, apiType string, maxTokens
|
||||
},
|
||||
Agents: agentsConfig{
|
||||
Defaults: agentDefaults{
|
||||
Model: modelRef{Primary: modelName},
|
||||
UserTimezone: resolveServerTimezone(),
|
||||
Model: modelRef{Primary: modelName},
|
||||
},
|
||||
},
|
||||
Browser: browserConfig{
|
||||
@@ -1677,6 +1737,9 @@ func writeOpenclawConfig(confDir, provider, modelName, apiType string, maxTokens
|
||||
}
|
||||
agentsMap := ensureChildMap(conf, "agents")
|
||||
defaultsMap := ensureChildMap(agentsMap, "defaults")
|
||||
if tz, ok := defaultsMap["userTimezone"]; !ok || strings.TrimSpace(fmt.Sprintf("%v", tz)) == "" {
|
||||
defaultsMap["userTimezone"] = resolveServerTimezone()
|
||||
}
|
||||
modelMap := ensureChildMap(defaultsMap, "model")
|
||||
modelMap["primary"] = cfg.Agents.Defaults.Model.Primary
|
||||
|
||||
@@ -1700,6 +1763,17 @@ func writeOpenclawConfig(confDir, provider, modelName, apiType string, maxTokens
|
||||
return fileOp.SaveFile(envPath, content, 0600)
|
||||
}
|
||||
|
||||
func resolveServerTimezone() string {
|
||||
timezone := strings.TrimSpace(common.LoadTimeZoneByCmd())
|
||||
if timezone == "" {
|
||||
return defaultUserTimezone
|
||||
}
|
||||
if _, err := time.LoadLocation(timezone); err != nil {
|
||||
return defaultUserTimezone
|
||||
}
|
||||
return timezone
|
||||
}
|
||||
|
||||
func ensureChildMap(parent map[string]interface{}, key string) map[string]interface{} {
|
||||
if child, ok := parent[key].(map[string]interface{}); ok {
|
||||
return child
|
||||
|
||||
@@ -60,6 +60,8 @@ func (a *AIToolsRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
aiToolsRouter.POST("/agents/channel/discord/update", baseApi.UpdateAgentDiscordConfig)
|
||||
aiToolsRouter.POST("/agents/browser/get", baseApi.GetAgentBrowserConfig)
|
||||
aiToolsRouter.POST("/agents/browser/update", baseApi.UpdateAgentBrowserConfig)
|
||||
aiToolsRouter.POST("/agents/other/get", baseApi.GetAgentOtherConfig)
|
||||
aiToolsRouter.POST("/agents/other/update", baseApi.UpdateAgentOtherConfig)
|
||||
aiToolsRouter.POST("/agents/channel/pairing/approve", baseApi.ApproveAgentChannelPairing)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -472,4 +472,17 @@ export namespace AI {
|
||||
noSandbox: boolean;
|
||||
defaultProfile: string;
|
||||
}
|
||||
|
||||
export interface AgentOtherConfigReq {
|
||||
agentId: number;
|
||||
}
|
||||
|
||||
export interface AgentOtherConfig {
|
||||
userTimezone: string;
|
||||
}
|
||||
|
||||
export interface AgentOtherConfigUpdateReq {
|
||||
agentId: number;
|
||||
userTimezone: string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,6 +173,14 @@ export const updateAgentBrowserConfig = (req: AI.AgentBrowserConfigUpdateReq) =>
|
||||
return http.post(`/ai/agents/browser/update`, req);
|
||||
};
|
||||
|
||||
export const getAgentOtherConfig = (req: AI.AgentOtherConfigReq) => {
|
||||
return http.post<AI.AgentOtherConfig>(`/ai/agents/other/get`, req);
|
||||
};
|
||||
|
||||
export const updateAgentOtherConfig = (req: AI.AgentOtherConfigUpdateReq) => {
|
||||
return http.post(`/ai/agents/other/update`, req);
|
||||
};
|
||||
|
||||
export const approveAgentChannelPairing = (req: AI.AgentChannelPairingApproveReq) => {
|
||||
return http.post(`/ai/agents/channel/pairing/approve`, req);
|
||||
};
|
||||
|
||||
@@ -698,6 +698,8 @@ const message = {
|
||||
configTitle: 'Configuration',
|
||||
settingsTab: 'Settings',
|
||||
browserTab: 'Browser',
|
||||
otherTab: 'Other',
|
||||
timeZone: 'Time Zone',
|
||||
browserEnabled: 'Browser Enabled',
|
||||
headless: 'Headless',
|
||||
noSandbox: 'No Sandbox',
|
||||
|
||||
@@ -694,6 +694,8 @@ const message = {
|
||||
configTitle: 'Configuration',
|
||||
settingsTab: 'Settings',
|
||||
browserTab: 'Browser',
|
||||
otherTab: 'Other',
|
||||
timeZone: 'Zona horaria',
|
||||
browserEnabled: 'Browser Enabled',
|
||||
headless: 'Headless',
|
||||
noSandbox: 'No Sandbox',
|
||||
|
||||
@@ -686,6 +686,8 @@ const message = {
|
||||
configTitle: 'Configuration',
|
||||
settingsTab: 'Settings',
|
||||
browserTab: 'Browser',
|
||||
otherTab: 'Other',
|
||||
timeZone: 'タイムゾーン',
|
||||
browserEnabled: 'Browser Enabled',
|
||||
headless: 'Headless',
|
||||
noSandbox: 'No Sandbox',
|
||||
|
||||
@@ -681,6 +681,8 @@ const message = {
|
||||
configTitle: 'Configuration',
|
||||
settingsTab: 'Settings',
|
||||
browserTab: 'Browser',
|
||||
otherTab: 'Other',
|
||||
timeZone: '시간대',
|
||||
browserEnabled: 'Browser Enabled',
|
||||
headless: 'Headless',
|
||||
noSandbox: 'No Sandbox',
|
||||
|
||||
@@ -697,6 +697,8 @@ const message = {
|
||||
configTitle: 'Configuration',
|
||||
settingsTab: 'Settings',
|
||||
browserTab: 'Browser',
|
||||
otherTab: 'Other',
|
||||
timeZone: 'Zon Waktu',
|
||||
browserEnabled: 'Browser Enabled',
|
||||
headless: 'Headless',
|
||||
noSandbox: 'No Sandbox',
|
||||
|
||||
@@ -694,6 +694,8 @@ const message = {
|
||||
configTitle: 'Configuration',
|
||||
settingsTab: 'Settings',
|
||||
browserTab: 'Browser',
|
||||
otherTab: 'Other',
|
||||
timeZone: 'Fuso horário',
|
||||
browserEnabled: 'Browser Enabled',
|
||||
headless: 'Headless',
|
||||
noSandbox: 'No Sandbox',
|
||||
|
||||
@@ -689,6 +689,8 @@ const message = {
|
||||
configTitle: 'Configuration',
|
||||
settingsTab: 'Settings',
|
||||
browserTab: 'Browser',
|
||||
otherTab: 'Other',
|
||||
timeZone: 'Часовой пояс',
|
||||
browserEnabled: 'Browser Enabled',
|
||||
headless: 'Headless',
|
||||
noSandbox: 'No Sandbox',
|
||||
|
||||
@@ -703,6 +703,8 @@ const message = {
|
||||
configTitle: 'Configuration',
|
||||
settingsTab: 'Settings',
|
||||
browserTab: 'Browser',
|
||||
otherTab: 'Other',
|
||||
timeZone: 'Saat Dilimi',
|
||||
browserEnabled: 'Browser Enabled',
|
||||
headless: 'Headless',
|
||||
noSandbox: 'No Sandbox',
|
||||
|
||||
@@ -672,6 +672,8 @@ const message = {
|
||||
configTitle: '設定',
|
||||
settingsTab: '設定',
|
||||
browserTab: '瀏覽器',
|
||||
otherTab: '其他',
|
||||
timeZone: '時區',
|
||||
browserEnabled: '瀏覽器開關',
|
||||
headless: '無頭模式',
|
||||
noSandbox: '禁用沙箱',
|
||||
|
||||
@@ -656,6 +656,8 @@ const message = {
|
||||
configTitle: '配置',
|
||||
settingsTab: '设置',
|
||||
browserTab: '浏览器',
|
||||
otherTab: '其他',
|
||||
timeZone: '时区',
|
||||
browserEnabled: '浏览器开关',
|
||||
headless: '无头模式',
|
||||
noSandbox: '禁用沙箱',
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
<el-tab-pane :label="t('aiTools.agents.browserTab')" name="browser">
|
||||
<BrowserTab ref="browserRef" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="t('aiTools.agents.otherTab')" name="other">
|
||||
<OtherTab ref="otherRef" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
|
||||
@@ -10,11 +13,13 @@
|
||||
import { nextTick, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import BrowserTab from './settings/browser.vue';
|
||||
import OtherTab from './settings/other.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const activeTab = ref('browser');
|
||||
const agentId = ref(0);
|
||||
const browserRef = ref();
|
||||
const otherRef = ref();
|
||||
|
||||
const loadCurrentTab = async () => {
|
||||
if (agentId.value <= 0) {
|
||||
@@ -23,6 +28,10 @@ const loadCurrentTab = async () => {
|
||||
await nextTick();
|
||||
if (activeTab.value === 'browser') {
|
||||
await browserRef.value?.load(agentId.value);
|
||||
return;
|
||||
}
|
||||
if (activeTab.value === 'other') {
|
||||
await otherRef.value?.load(agentId.value);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-position="top" v-loading="loading">
|
||||
<el-form-item :label="t('aiTools.agents.timeZone')" prop="userTimezone">
|
||||
<el-input v-model="form.userTimezone" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="saving" @click="saveConfig">
|
||||
{{ t('commons.button.save') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from 'vue';
|
||||
import type { FormInstance } from 'element-plus';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import { AI } from '@/api/interface/ai';
|
||||
import { getAgentOtherConfig, updateAgentOtherConfig } from '@/api/modules/ai';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
|
||||
const { t } = useI18n();
|
||||
const loading = ref(false);
|
||||
const saving = ref(false);
|
||||
const agentId = ref(0);
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
const form = reactive<AI.AgentOtherConfig>({
|
||||
userTimezone: '',
|
||||
});
|
||||
|
||||
const rules = reactive({
|
||||
userTimezone: [Rules.requiredInput],
|
||||
});
|
||||
|
||||
const load = async (id: number) => {
|
||||
agentId.value = id;
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getAgentOtherConfig({ agentId: id });
|
||||
Object.assign(form, res.data || {});
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const saveConfig = async () => {
|
||||
if (!agentId.value || !formRef.value) {
|
||||
return;
|
||||
}
|
||||
await formRef.value.validate();
|
||||
saving.value = true;
|
||||
try {
|
||||
await updateAgentOtherConfig({
|
||||
agentId: agentId.value,
|
||||
userTimezone: form.userTimezone,
|
||||
});
|
||||
MsgSuccess(t('aiTools.agents.saveSuccess'));
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
load,
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user