myagent-ai 1.15.78 → 1.15.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agents/main_agent.py +3 -2
- package/config.py +1 -0
- package/core/context_builder.py +5 -2
- package/core/utils.py +18 -2
- package/memory/manager.py +3 -2
- package/package.json +1 -1
- package/web/api_server.py +39 -9
- package/web/ui/index.html +30 -20
package/agents/main_agent.py
CHANGED
|
@@ -280,8 +280,9 @@ class MainAgent(BaseAgent):
|
|
|
280
280
|
return None
|
|
281
281
|
|
|
282
282
|
from datetime import datetime
|
|
283
|
+
from core.utils import get_config_tz
|
|
283
284
|
old_time = old_memory.created_at or "未知时间"
|
|
284
|
-
new_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
285
|
+
new_time = datetime.now(get_config_tz()).strftime("%Y-%m-%d %H:%M:%S")
|
|
285
286
|
user_msg = context.user_message or ""
|
|
286
287
|
|
|
287
288
|
merge_prompt = f"""你是一个记忆管理系统。现在系统检测到两条高度相似的记忆,请你判断如何合并它们。
|
|
@@ -377,7 +378,7 @@ class MainAgent(BaseAgent):
|
|
|
377
378
|
safe_session = session_id.replace("-", "").replace("/", "_")[:12] if session_id else "default"
|
|
378
379
|
kb_file = auto_kb_dir / f"{safe_session}.md"
|
|
379
380
|
|
|
380
|
-
now_str = datetime.now().strftime("%Y-%m-%d %H:%M")
|
|
381
|
+
now_str = datetime.now(get_config_tz()).strftime("%Y-%m-%d %H:%M")
|
|
381
382
|
|
|
382
383
|
# 检查重复:与已有文件内容做相似度比较
|
|
383
384
|
existing_content = ""
|
package/config.py
CHANGED
|
@@ -150,6 +150,7 @@ class AppConfig:
|
|
|
150
150
|
log_level: str = "INFO"
|
|
151
151
|
data_dir: str = "" # 数据目录,默认 ~/.myagent/
|
|
152
152
|
language: str = "zh-CN"
|
|
153
|
+
timezone: str = "Asia/Shanghai" # 时区,用于生成时间戳和提示词中的当前时间
|
|
153
154
|
|
|
154
155
|
|
|
155
156
|
# ==============================================================================
|
package/core/context_builder.py
CHANGED
|
@@ -191,9 +191,12 @@ class ContextBuilder:
|
|
|
191
191
|
"""
|
|
192
192
|
构建 <datetime> 段落 —— 当前日期时间(精确到秒)。
|
|
193
193
|
让 LLM 知道当前时间,以便给出与时间相关的回答。
|
|
194
|
+
使用配置的时区,而非系统时区。
|
|
194
195
|
"""
|
|
196
|
+
from core.utils import get_config_tz
|
|
195
197
|
from datetime import datetime
|
|
196
|
-
|
|
198
|
+
tz = get_config_tz()
|
|
199
|
+
now = datetime.now(tz)
|
|
197
200
|
weekdays = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"]
|
|
198
201
|
date_str = now.strftime("%Y年%m月%d日")
|
|
199
202
|
time_str = now.strftime("%H:%M:%S")
|
|
@@ -201,7 +204,7 @@ class ContextBuilder:
|
|
|
201
204
|
return (
|
|
202
205
|
f"<datetime>\n"
|
|
203
206
|
f"当前时间: {date_str} {weekday} {time_str}\n"
|
|
204
|
-
f"
|
|
207
|
+
f"时区: {tz}\n"
|
|
205
208
|
f"</datetime>"
|
|
206
209
|
)
|
|
207
210
|
|
package/core/utils.py
CHANGED
|
@@ -9,14 +9,30 @@ import uuid
|
|
|
9
9
|
import time
|
|
10
10
|
import re
|
|
11
11
|
from datetime import datetime, timezone
|
|
12
|
+
from zoneinfo import ZoneInfo
|
|
12
13
|
from typing import Any, Dict, Optional, TypeVar
|
|
13
14
|
|
|
14
15
|
T = TypeVar("T")
|
|
15
16
|
|
|
17
|
+
# 时区缓存,避免每次调用都重新解析
|
|
18
|
+
_tz_cache: Optional[ZoneInfo] = None
|
|
19
|
+
|
|
20
|
+
def get_config_tz() -> ZoneInfo:
|
|
21
|
+
"""获取配置的时区对象(带缓存)"""
|
|
22
|
+
global _tz_cache
|
|
23
|
+
if _tz_cache is not None:
|
|
24
|
+
return _tz_cache
|
|
25
|
+
try:
|
|
26
|
+
from config import ConfigManager
|
|
27
|
+
tz_name = ConfigManager().get("timezone", "Asia/Shanghai")
|
|
28
|
+
_tz_cache = ZoneInfo(tz_name)
|
|
29
|
+
except Exception:
|
|
30
|
+
_tz_cache = ZoneInfo("Asia/Shanghai")
|
|
31
|
+
return _tz_cache
|
|
16
32
|
|
|
17
33
|
def timestamp() -> str:
|
|
18
|
-
"""返回 ISO 8601
|
|
19
|
-
return datetime.now().isoformat()
|
|
34
|
+
"""返回 ISO 8601 格式时间戳(使用配置时区)"""
|
|
35
|
+
return datetime.now(get_config_tz()).isoformat()
|
|
20
36
|
|
|
21
37
|
|
|
22
38
|
def timestamp_ms() -> int:
|
package/memory/manager.py
CHANGED
|
@@ -473,7 +473,8 @@ class MemoryManager:
|
|
|
473
473
|
def add_global(self, session_id="global", key="", content="", summary="", importance=0.7, metadata=None) -> str:
|
|
474
474
|
"""添加全局记忆(跨会话可检索)"""
|
|
475
475
|
from datetime import datetime
|
|
476
|
-
|
|
476
|
+
from core.utils import get_config_tz
|
|
477
|
+
now_str = datetime.now(get_config_tz()).strftime("%Y-%m-%d %H:%M:%S")
|
|
477
478
|
ts_summary = summary or truncate_str(content, 200)
|
|
478
479
|
entry = MemoryEntry(
|
|
479
480
|
session_id=session_id, category="global", key=key,
|
|
@@ -676,7 +677,7 @@ class MemoryManager:
|
|
|
676
677
|
try:
|
|
677
678
|
# 解析 ISO 8601 时间戳
|
|
678
679
|
created_dt = datetime.fromisoformat(created_at.replace("Z", "+00:00"))
|
|
679
|
-
now_dt = datetime.now(
|
|
680
|
+
now_dt = datetime.now(get_config_tz())
|
|
680
681
|
|
|
681
682
|
age_seconds = (now_dt - created_dt).total_seconds()
|
|
682
683
|
if age_seconds <= 0:
|
package/package.json
CHANGED
package/web/api_server.py
CHANGED
|
@@ -18,6 +18,11 @@ from web.tts_handler import synthesize, preprocess_for_tts, AVAILABLE_VOICES
|
|
|
18
18
|
|
|
19
19
|
logger = get_logger("myagent.api")
|
|
20
20
|
|
|
21
|
+
def _now_iso():
|
|
22
|
+
"""返回配置时区的 ISO 时间戳"""
|
|
23
|
+
from core.utils import get_config_tz
|
|
24
|
+
return datetime.datetime.now(get_config_tz()).isoformat()
|
|
25
|
+
|
|
21
26
|
def _safe_load_json(filepath, default=None):
|
|
22
27
|
"""安全读取 JSON 文件,解析失败返回默认值"""
|
|
23
28
|
try:
|
|
@@ -339,6 +344,8 @@ class ApiServer:
|
|
|
339
344
|
r.add_post("/api/knowledge/search", self.handle_knowledge_search)
|
|
340
345
|
# ── 配置管理 (热重载/导入/导出) ──
|
|
341
346
|
r.add_get("/api/config", self.handle_get_config)
|
|
347
|
+
r.add_post("/api/config/get", self.handle_get_config_key)
|
|
348
|
+
r.add_post("/api/config/set", self.handle_set_config_key)
|
|
342
349
|
r.add_post("/api/config/reload", self.handle_reload_config)
|
|
343
350
|
r.add_post("/api/config/export", self.handle_export_config)
|
|
344
351
|
r.add_post("/api/config/import", self.handle_import_config)
|
|
@@ -1619,7 +1626,7 @@ class ApiServer:
|
|
|
1619
1626
|
# 迁移后再次检查(损坏文件可能已被删除)
|
|
1620
1627
|
if not (ad / "config.json").exists():
|
|
1621
1628
|
ad.mkdir(parents=True, exist_ok=True)
|
|
1622
|
-
now =
|
|
1629
|
+
now = _now_iso()
|
|
1623
1630
|
cfg = {
|
|
1624
1631
|
"id": uuid.uuid4().hex[:12],
|
|
1625
1632
|
"name": "全权Agent",
|
|
@@ -1667,7 +1674,7 @@ class ApiServer:
|
|
|
1667
1674
|
# 其他 agent:创建一个最小 config
|
|
1668
1675
|
ad = self._agent_dir(path)
|
|
1669
1676
|
if ad.exists():
|
|
1670
|
-
now =
|
|
1677
|
+
now = _now_iso()
|
|
1671
1678
|
cfg = {
|
|
1672
1679
|
"id": uuid.uuid4().hex[:12],
|
|
1673
1680
|
"name": path,
|
|
@@ -1682,7 +1689,7 @@ class ApiServer:
|
|
|
1682
1689
|
logger.info(f"已重建 Agent 配置: {path}")
|
|
1683
1690
|
return
|
|
1684
1691
|
changed = False
|
|
1685
|
-
now =
|
|
1692
|
+
now = _now_iso()
|
|
1686
1693
|
if "id" not in cfg:
|
|
1687
1694
|
cfg["id"] = uuid.uuid4().hex[:12]
|
|
1688
1695
|
changed = True
|
|
@@ -1708,7 +1715,7 @@ class ApiServer:
|
|
|
1708
1715
|
# 迁移后再次检查(损坏文件可能已被删除)
|
|
1709
1716
|
if not (ad / "config.json").exists():
|
|
1710
1717
|
ad.mkdir(parents=True, exist_ok=True)
|
|
1711
|
-
now =
|
|
1718
|
+
now = _now_iso()
|
|
1712
1719
|
cfg = {
|
|
1713
1720
|
"id": uuid.uuid4().hex[:12],
|
|
1714
1721
|
"name": "配置助手",
|
|
@@ -1737,7 +1744,7 @@ class ApiServer:
|
|
|
1737
1744
|
cfg = json.loads((ad / "config.json").read_text(encoding="utf-8"))
|
|
1738
1745
|
if cfg.get("system_prompt") != CONFIG_HELPER_PROMPT:
|
|
1739
1746
|
cfg["system_prompt"] = CONFIG_HELPER_PROMPT
|
|
1740
|
-
cfg["updated_at"] =
|
|
1747
|
+
cfg["updated_at"] = _now_iso()
|
|
1741
1748
|
(ad / "config.json").write_text(json.dumps(cfg, indent=2, ensure_ascii=False), encoding="utf-8")
|
|
1742
1749
|
logger.info("已同步配置助手最新的系统提示词")
|
|
1743
1750
|
except Exception as e:
|
|
@@ -1962,7 +1969,7 @@ class ApiServer:
|
|
|
1962
1969
|
if (ad / "config.json").exists():
|
|
1963
1970
|
return web.json_response({"error": f"Agent '{name}' already exists"}, status=409)
|
|
1964
1971
|
|
|
1965
|
-
now =
|
|
1972
|
+
now = _now_iso()
|
|
1966
1973
|
cfg = {
|
|
1967
1974
|
"id": uuid.uuid4().hex[:12],
|
|
1968
1975
|
"name": name,
|
|
@@ -2186,7 +2193,7 @@ class ApiServer:
|
|
|
2186
2193
|
if "system" in data:
|
|
2187
2194
|
del data["system"]
|
|
2188
2195
|
# 自动更新 updated_at
|
|
2189
|
-
cfg["updated_at"] =
|
|
2196
|
+
cfg["updated_at"] = _now_iso()
|
|
2190
2197
|
(ad / "config.json").write_text(json.dumps(cfg, indent=2, ensure_ascii=False), encoding="utf-8")
|
|
2191
2198
|
# 部门变更时同步部门成员列表(old_dept 已在 cfg 修改前保存)
|
|
2192
2199
|
new_dept = data.get("department", old_dept)
|
|
@@ -3316,6 +3323,28 @@ class ApiServer:
|
|
|
3316
3323
|
cfg = self.core.config_mgr.get_full_config()
|
|
3317
3324
|
return web.json_response(cfg)
|
|
3318
3325
|
|
|
3326
|
+
async def handle_get_config_key(self, request):
|
|
3327
|
+
"""POST /api/config/get - 获取单个配置项"""
|
|
3328
|
+
data = await request.json()
|
|
3329
|
+
key = data.get("key", "")
|
|
3330
|
+
value = getattr(self.core.config, key, None)
|
|
3331
|
+
return web.json_response({"ok": True, "value": value or ""})
|
|
3332
|
+
|
|
3333
|
+
async def handle_set_config_key(self, request):
|
|
3334
|
+
"""POST /api/config/set - 设置单个配置项并保存"""
|
|
3335
|
+
data = await request.json()
|
|
3336
|
+
key = data.get("key", "")
|
|
3337
|
+
value = data.get("value", "")
|
|
3338
|
+
if not key:
|
|
3339
|
+
return web.json_response({"ok": False, "error": "缺少 key"})
|
|
3340
|
+
setattr(self.core.config, key, value)
|
|
3341
|
+
self.core.config_mgr.save()
|
|
3342
|
+
# 时区变更时清除缓存,下次调用 get_config_tz() 立即生效
|
|
3343
|
+
if key == "timezone":
|
|
3344
|
+
import core.utils as _u
|
|
3345
|
+
_u._tz_cache = None
|
|
3346
|
+
return web.json_response({"ok": True})
|
|
3347
|
+
|
|
3319
3348
|
def _build_model_chain(self, agent_cfg: dict | None, agent_path: str) -> list[dict]:
|
|
3320
3349
|
"""构建模型链: [主模型, 备用模型1, 备用模型2, ...]"""
|
|
3321
3350
|
if not agent_cfg:
|
|
@@ -4697,8 +4726,9 @@ class ApiServer:
|
|
|
4697
4726
|
include_secrets = data.get("include_secrets", False)
|
|
4698
4727
|
|
|
4699
4728
|
try:
|
|
4729
|
+
from core.utils import get_config_tz
|
|
4700
4730
|
export_data = self.core.config_mgr.export_config(include_secrets=include_secrets)
|
|
4701
|
-
filename = f"myagent_config_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
|
|
4731
|
+
filename = f"myagent_config_{datetime.datetime.now(get_config_tz()).strftime('%Y%m%d_%H%M%S')}.json"
|
|
4702
4732
|
|
|
4703
4733
|
resp = web.Response(
|
|
4704
4734
|
body=json.dumps(export_data, ensure_ascii=False, indent=2),
|
|
@@ -6221,7 +6251,7 @@ class ApiServer:
|
|
|
6221
6251
|
self._execution_lock = {
|
|
6222
6252
|
"locked": True,
|
|
6223
6253
|
"locked_by": agent_path,
|
|
6224
|
-
"locked_at":
|
|
6254
|
+
"locked_at": _now_iso(),
|
|
6225
6255
|
}
|
|
6226
6256
|
logger.info(f"全局执行锁已获取: {agent_path}")
|
|
6227
6257
|
return web.json_response({
|
package/web/ui/index.html
CHANGED
|
@@ -2005,10 +2005,13 @@ async function uploadDeptKB(path,folderMode){
|
|
|
2005
2005
|
async function renderSystem(){
|
|
2006
2006
|
$('content').innerHTML=`
|
|
2007
2007
|
<div class="card">
|
|
2008
|
-
<h3
|
|
2009
|
-
<p style="font-size:13px;color:var(--text2);margin-bottom:12px"
|
|
2010
|
-
<
|
|
2011
|
-
|
|
2008
|
+
<h3>🕐 时区设置</h3>
|
|
2009
|
+
<p style="font-size:13px;color:var(--text2);margin-bottom:12px">设置系统时区,影响提示词中的当前时间、记忆时间戳、日志时间等所有时间显示</p>
|
|
2010
|
+
<div style="display:flex;align-items:center;gap:12px;margin-bottom:12px">
|
|
2011
|
+
<select id="sysTimezone" style="flex:1;padding:8px 12px;border:1px solid var(--border);border-radius:6px;background:var(--bg2);color:var(--text);font-size:14px"></select>
|
|
2012
|
+
<button class="btn btn-primary" id="sysTzSaveBtn" onclick="sysSaveTimezone()">保存</button>
|
|
2013
|
+
</div>
|
|
2014
|
+
<div id="sysTzMsg"></div>
|
|
2012
2015
|
</div>
|
|
2013
2016
|
<div class="card">
|
|
2014
2017
|
<h3>📤 导出备份</h3>
|
|
@@ -2038,31 +2041,38 @@ async function renderSystem(){
|
|
|
2038
2041
|
<h3>👁️ 配置预览</h3>
|
|
2039
2042
|
<div class="config-preview" id="sysConfigPreview">加载中...</div>
|
|
2040
2043
|
</div>`;
|
|
2044
|
+
sysLoadTimezone();
|
|
2041
2045
|
sysLoadPreview();
|
|
2042
2046
|
}
|
|
2043
2047
|
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
+
const COMMON_TIMEZONES=['Asia/Shanghai','Asia/Kuala_Lumpur','Asia/Singapore','Asia/Tokyo','Asia/Seoul','Asia/Ho_Chi_Minh','Asia/Bangkok','Asia/Jakarta','Asia/Kolkata','Asia/Dubai','Europe/London','Europe/Paris','Europe/Berlin','Europe/Moscow','America/New_York','America/Chicago','America/Denver','America/Los_Angeles','Australia/Sydney','Pacific/Auckland','UTC'];
|
|
2049
|
+
|
|
2050
|
+
async function sysLoadTimezone(){
|
|
2051
|
+
try{
|
|
2052
|
+
const r=await api('/api/config/get',{method:'POST',body:JSON.stringify({key:'timezone'})});
|
|
2053
|
+
const sel=$('sysTimezone');
|
|
2054
|
+
sel.innerHTML=COMMON_TIMEZONES.map(tz=>`<option value="${tz}"${tz===(r.value||'Asia/Shanghai')?' selected':''}>${tz}</option>`).join('');
|
|
2055
|
+
}catch(e){console.error('Load timezone failed:',e);}
|
|
2048
2056
|
}
|
|
2049
2057
|
|
|
2050
|
-
async function
|
|
2051
|
-
const btn=$('
|
|
2052
|
-
sysSetMsg('
|
|
2058
|
+
async function sysSaveTimezone(){
|
|
2059
|
+
const btn=$('sysTzSaveBtn');btn.disabled=true;
|
|
2060
|
+
sysSetMsg('sysTzMsg','loading','正在保存时区...');
|
|
2053
2061
|
try{
|
|
2054
|
-
const
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
sysSetMsg('sysReloadMsg','success',msg);showToast(msg,'success');
|
|
2060
|
-
sysLoadPreview();
|
|
2061
|
-
}else{sysSetMsg('sysReloadMsg','error','❌ 热重载失败: '+(r.error||'未知错误'));}
|
|
2062
|
-
}catch(e){sysSetMsg('sysReloadMsg','error','❌ 热重载失败: '+e.message);}
|
|
2062
|
+
const tz=$('sysTimezone').value;
|
|
2063
|
+
const r=await api('/api/config/set',{method:'POST',body:JSON.stringify({key:'timezone',value:tz})});
|
|
2064
|
+
if(r.ok){sysSetMsg('sysTzMsg','success','✅ 时区已保存为 '+tz);showToast('时区已更新为 '+tz,'success');}
|
|
2065
|
+
else{sysSetMsg('sysTzMsg','error','❌ 保存失败: '+(r.error||'未知错误'));}
|
|
2066
|
+
}catch(e){sysSetMsg('sysTzMsg','error','❌ 保存失败: '+e.message);}
|
|
2063
2067
|
btn.disabled=false;
|
|
2064
2068
|
}
|
|
2065
2069
|
|
|
2070
|
+
function sysSetMsg(id,type,msg){
|
|
2071
|
+
const el=$(id);if(!el)return;
|
|
2072
|
+
el.className='status-msg '+type;el.textContent=msg;
|
|
2073
|
+
if(type!=='loading')setTimeout(()=>{if(el.className.includes(type))el.className='status-msg';},8000);
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2066
2076
|
async function sysExport(includeSecrets){
|
|
2067
2077
|
const btnId=includeSecrets?'sysExportFull':'sysExportSafe';
|
|
2068
2078
|
const btn=$(btnId);btn.disabled=true;
|