feat(stapp): empty-key boot and portal hot-reload wait
Allow GenericAgent to start with empty mykey, watch subscription portal writes via list_llms, and app-rerun so sidebar LLM select refreshes without a manual click. Also harden mykey reimport and note the fastest tmwebdriver autofill login path.
This commit is contained in:
1 parent
8a75b39bf8
commit
400c9ef34f
4 files changed
+36
-21
No files matched your search
+5
-4
@@ -55,6 +55,7 @@ class GenericAgent:
|
||||
self.force_non_stream = False
|
||||
logid = f'{(time.time_ns() + random.randrange(1_000_000)) % 1_000_000:06d}'
|
||||
self.log_path = os.path.join(script_dir, f'temp/model_responses/model_responses_{logid}.txt')
|
||||
self.llmclient = None
|
||||
self.load_llm_sessions()
|
||||
self.extra_sys_prompts = []
|
||||
self.intervene = self.extrakeyinfo = None
|
||||
@@ -79,20 +80,20 @@ class GenericAgent:
|
||||
else: llm_sessions[i] = ToolClient(mixin)
|
||||
except Exception as e: print(f'\n\n\n[ERROR] Failed to init MixinSession with cfg {s["mixin_cfg"]}: {e}!!!\n\n')
|
||||
self.llmclients = llm_sessions
|
||||
if not self.llmclients: return
|
||||
self.llmclient = self.llmclients[self.llm_no%len(self.llmclients)]
|
||||
if oldhistory: self.llmclient.backend.history = oldhistory
|
||||
|
||||
def next_llm(self, n=-1):
|
||||
self.load_llm_sessions()
|
||||
if not self.llmclients: return
|
||||
self.llm_no = ((self.llm_no + 1) if n < 0 else n) % len(self.llmclients)
|
||||
lastc = self.llmclient
|
||||
self.llmclient = self.llmclients[self.llm_no]
|
||||
try: self.llmclient.backend.history = lastc.backend.history
|
||||
except: raise Exception('[ERROR] BAD Mixin config: Check your mykey.py')
|
||||
self.llmclient.last_tools = ''
|
||||
name = self.get_llm_name(model=True)
|
||||
if 'glm' in name or 'minimax' in name or 'kimi' in name: load_tool_schema('_cn')
|
||||
else: load_tool_schema()
|
||||
load_tool_schema()
|
||||
def list_llms(self):
|
||||
self.load_llm_sessions()
|
||||
return [(i, self.get_llm_name(b), i == self.llm_no) for i, b in enumerate(self.llmclients)]
|
||||
@@ -100,7 +101,7 @@ class GenericAgent:
|
||||
b = self.llmclient if b is None else b
|
||||
if isinstance(b, dict): return 'BADCONFIG_MIXIN'
|
||||
if model: return b.backend.model.lower()
|
||||
return f"{type(b.backend).__name__}/{b.backend.name}"
|
||||
return f"{type(b.backend).__name__.replace('Session', '')}/{b.backend.name}"
|
||||
def get_ctx_multiplier(self): return getattr(self.llmclient.backend, 'maxlen_multiplier', 1.0)
|
||||
|
||||
def abort(self):
|
||||
|
||||
+24
-14
@@ -15,7 +15,7 @@ import streamlit as st
|
||||
import time, json, re, threading, queue
|
||||
from datetime import timedelta
|
||||
import agentmain
|
||||
from agentmain import GeneraticAgent
|
||||
from agentmain import GenericAgent
|
||||
import chatapp_common # activate /continue command (monkey patches GeneraticAgent)
|
||||
from continue_cmd import handle_frontend_command, reset_conversation, list_sessions, extract_ui_messages
|
||||
from btw_cmd import handle_frontend_command as btw_handle_frontend
|
||||
@@ -73,19 +73,28 @@ def T(key): return I18N.get(LANG, I18N['zh']).get(key, key)
|
||||
|
||||
@st.cache_resource
|
||||
def init():
|
||||
agent = GeneraticAgent()
|
||||
if agent.llmclient is not None:
|
||||
threading.Thread(target=agent.run, daemon=True).start()
|
||||
agent = GenericAgent()
|
||||
threading.Thread(target=agent.run, daemon=True).start()
|
||||
return agent
|
||||
|
||||
agent = init()
|
||||
if agent.llmclient is None:
|
||||
_sp = getattr(agentmain, "start_subscription_portal", None)
|
||||
if _sp:
|
||||
_sp()
|
||||
st.warning(T("reopen_page"))
|
||||
else:
|
||||
st.error(T("need_mykey"))
|
||||
_sp = getattr(agentmain, "start_subscription_portal", None)
|
||||
|
||||
@st.fragment(run_every=timedelta(seconds=2))
|
||||
def _watch_portal():
|
||||
b = st.session_state["portal_wait"]
|
||||
c = tuple(n for _, n, _ in agent.list_llms())
|
||||
if c and c != b:
|
||||
del st.session_state["portal_wait"]
|
||||
st.session_state.pop("sidebar_llm_select", None)
|
||||
st.rerun(scope="app")
|
||||
|
||||
if not agent.llmclients and _sp:
|
||||
st.session_state.setdefault("portal_wait", ()); _sp()
|
||||
if "portal_wait" in st.session_state: _watch_portal()
|
||||
|
||||
if not agent.llmclients:
|
||||
st.warning(T("reopen_page")) if _sp else st.error(T("need_mykey"))
|
||||
st.stop()
|
||||
|
||||
def build_prompt(objective):
|
||||
@@ -101,7 +110,7 @@ def build_prompt(objective):
|
||||
def get_controller():
|
||||
b = {'ev': threading.Event(), 'obj': '', 'out': None, 'ready': False}
|
||||
def loop():
|
||||
ag = GeneraticAgent(); ag.verbose = False; ag.log_path = False
|
||||
ag = GenericAgent(); ag.verbose = False; ag.log_path = False
|
||||
threading.Thread(target=ag.run, daemon=True).start()
|
||||
while True:
|
||||
b['ev'].wait(); b['ev'].clear()
|
||||
@@ -185,10 +194,11 @@ def render_sidebar():
|
||||
st.session_state.autonomous_enabled = True
|
||||
st.toast("✅"); st.rerun(scope="app")
|
||||
st.caption(T('auto_off_cap'))
|
||||
if (_sp := getattr(agentmain, "start_subscription_portal", None)):
|
||||
if _sp:
|
||||
st.divider()
|
||||
if st.button(T("get_token")):
|
||||
_sp(); st.toast(T("get_token_toast"))
|
||||
st.session_state.portal_wait = tuple(n for _, n, _ in agent.list_llms())
|
||||
_sp(); st.rerun(scope="app")
|
||||
with st.sidebar: render_sidebar()
|
||||
|
||||
def fold_turns(text):
|
||||
|
||||
+6
-2
@@ -8,7 +8,8 @@ if _ROOT not in sys.path: sys.path.append(_ROOT)
|
||||
def _load_mykeys():
|
||||
global _mykey_path
|
||||
try:
|
||||
import mykey; importlib.reload(mykey); _mykey_path = mykey.__file__
|
||||
sys.modules.pop('mykey', None)
|
||||
import mykey; _mykey_path = mykey.__file__
|
||||
return {k: v for k, v in vars(mykey).items() if not k.startswith('_')}
|
||||
except ImportError as e:
|
||||
if getattr(e, 'name', None) != 'mykey':
|
||||
@@ -21,13 +22,15 @@ def _load_mykeys():
|
||||
if isinstance(mk, dict) and 'remote_url' in mk: return requests.get(mk['remote_url'], timeout=10).json()
|
||||
return mk
|
||||
|
||||
_mykey_lock = threading.Lock()
|
||||
_mykey_path = _mykey_mtime = None
|
||||
def reload_mykeys():
|
||||
global _mykey_mtime
|
||||
try:
|
||||
mt = os.stat(_mykey_path).st_mtime_ns if _mykey_path else -1
|
||||
if mt == _mykey_mtime: return globals().get('mykeys', {}), False
|
||||
mk = _load_mykeys(); _mykey_mtime = os.stat(_mykey_path).st_mtime_ns
|
||||
with _mykey_lock: mk = _load_mykeys()
|
||||
_mykey_mtime = os.stat(_mykey_path).st_mtime_ns
|
||||
print(f'[Info] Load mykeys from {_mykey_path}')
|
||||
globals().update(mykeys=mk)
|
||||
return mk, True
|
||||
@@ -1122,6 +1125,7 @@ class NativeToolClient:
|
||||
def resolve_session(cfg_name):
|
||||
cfg = reload_mykeys()[0].get(cfg_name)
|
||||
if not cfg: raise ValueError(f"Config '{cfg_name}' not in mykey")
|
||||
cfg['_mykey_name'] = cfg_name
|
||||
if 'native' in cfg_name: return (NativeClaudeSession if 'claude' in cfg_name else NativeOAISession)(cfg=cfg)
|
||||
if 'claude' in cfg_name: return ClaudeSession(cfg=cfg)
|
||||
return LLMSession(cfg=cfg) if 'oai' in cfg_name else None
|
||||
|
||||
@@ -117,7 +117,7 @@ web_execute_js script='{"cmd": "batch", "commands": [...]}'
|
||||
## autofill获取与登录
|
||||
检测:web_scan输出input带`data-autofilled="true"`,value显示为受保护提示(非真实值,Chrome安全保护需点击释放)
|
||||
- ⚠**前置条件:必须先CDP `Page.bringToFront` 切tab到前台**,Chrome仅在前台tab释放autofill保护值,后台tab物理点击无效
|
||||
- ⭐**一键释放与登录**:bringToFront → mousePressed点任一字段(无需Released,一个释放全页) → 等500ms → 补input/change事件 → 点登录
|
||||
- ⭐**最快登录(✅已验证)**:bringToFront → 无视按钮`disabled`,直接CDP完整点击登录按钮(无需处理字段);失败再兜底:mousePressed任一字段 → 等500ms → 补input/change → 点登录
|
||||
|
||||
## 验证码/页面视觉截图
|
||||
- ⭐首选CDP截图:`Page.captureScreenshot`(format:'png')→返回base64,无需前台/后台tab也行,全页高清
|
||||
|
||||
Reference in new issue
Block a user