refactor: plugins dir + opt-in langfuse via __getattr__ guard
- mv langfuse_tracing.py -> plugins/langfuse_tracing.py - llmcore: load plugin lazily inside __getattr__ when langfuse_config present (PEP 562 module __getattr__ naturally fires only once after globals().update) - llmcore: extract _record_usage() from 4 scattered [Cache] print sites - agentmain: /resume scans only latest 10 files
This commit is contained in:
@@ -111,7 +111,7 @@ class GeneraticAgent:
|
|||||||
display_queue.put({'done': smart_format(f"✅ session.{k} = {repr(v)}", max_str_len=500), 'source': 'system'})
|
display_queue.put({'done': smart_format(f"✅ session.{k} = {repr(v)}", max_str_len=500), 'source': 'system'})
|
||||||
return None
|
return None
|
||||||
if raw_query.strip() == '/resume':
|
if raw_query.strip() == '/resume':
|
||||||
return r'用re.findall(r"<history>\\n\[(?:USER\|Agent)\].*?</history>", content, re.DOTALL) 扫temp/model_responses/下各文件(除本PID),取每文件最后一个匹配(注意JSON里换行是字面\\n)作为该会话内容,按mtime倒序,每个用一句话总结聊了什么让我选择;选定后再简单读该文件末尾作为聊天基础'
|
return r'用re.findall(r"<history>\\n\[(?:USER\|Agent)\].*?</history>", content, re.DOTALL) 扫temp/model_responses/下时间最近的10个文件(除本PID),取每文件最后一个匹配(注意JSON里换行是字面\\n)作为该会话内容,按mtime倒序,每个用一句话总结聊了什么让我选择;选定后再简单读该文件末尾作为聊天基础'
|
||||||
return raw_query
|
return raw_query
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|||||||
48
llmcore.py
48
llmcore.py
@@ -11,12 +11,15 @@ def _load_mykeys():
|
|||||||
if not os.path.exists(p): raise Exception('[ERROR] mykey.py or mykey.json not found, please create one from mykey_template.')
|
if not os.path.exists(p): raise Exception('[ERROR] mykey.py or mykey.json not found, please create one from mykey_template.')
|
||||||
with open(p, encoding='utf-8') as f: return json.load(f)
|
with open(p, encoding='utf-8') as f: return json.load(f)
|
||||||
|
|
||||||
def __getattr__(name):
|
def __getattr__(name): # once guard in PEP 562
|
||||||
if name in ('mykeys', 'proxies'):
|
if name in ('mykeys', 'proxies'):
|
||||||
mk = _load_mykeys()
|
mk = _load_mykeys()
|
||||||
proxy = mk.get("proxy", 'http://127.0.0.1:2082')
|
proxy = mk.get("proxy", 'http://127.0.0.1:2082')
|
||||||
px = {"http": proxy, "https": proxy} if proxy else None
|
px = {"http": proxy, "https": proxy} if proxy else None
|
||||||
globals().update(mykeys=mk, proxies=px)
|
globals().update(mykeys=mk, proxies=px)
|
||||||
|
if mk.get('langfuse_config'):
|
||||||
|
try: from plugins import langfuse_tracing
|
||||||
|
except Exception: pass
|
||||||
return globals()[name]
|
return globals()[name]
|
||||||
raise AttributeError(f"module 'llmcore' has no attribute {name}")
|
raise AttributeError(f"module 'llmcore' has no attribute {name}")
|
||||||
|
|
||||||
@@ -108,8 +111,7 @@ def _parse_claude_sse(resp_lines):
|
|||||||
evt_type = evt.get("type", "")
|
evt_type = evt.get("type", "")
|
||||||
if evt_type == "message_start":
|
if evt_type == "message_start":
|
||||||
usage = evt.get("message", {}).get("usage", {})
|
usage = evt.get("message", {}).get("usage", {})
|
||||||
ci, cr, inp = usage.get("cache_creation_input_tokens", 0), usage.get("cache_read_input_tokens", 0), usage.get("input_tokens", 0)
|
_record_usage(usage, "messages")
|
||||||
print(f"[Cache] input={inp} creation={ci} read={cr}")
|
|
||||||
elif evt_type == "content_block_start":
|
elif evt_type == "content_block_start":
|
||||||
block = evt.get("content_block", {})
|
block = evt.get("content_block", {})
|
||||||
if block.get("type") == "text": current_block = {"type": "text", "text": ""}
|
if block.get("type") == "text": current_block = {"type": "text", "text": ""}
|
||||||
@@ -194,9 +196,7 @@ def _parse_openai_sse(resp_lines, api_mode="chat_completions"):
|
|||||||
break
|
break
|
||||||
elif etype == "response.completed":
|
elif etype == "response.completed":
|
||||||
usage = evt.get("response", {}).get("usage", {})
|
usage = evt.get("response", {}).get("usage", {})
|
||||||
cached = (usage.get("input_tokens_details") or {}).get("cached_tokens", 0)
|
_record_usage(usage, api_mode)
|
||||||
inp = usage.get("input_tokens", 0)
|
|
||||||
if inp: print(f"[Cache] input={inp} cached={cached}")
|
|
||||||
break
|
break
|
||||||
blocks = []
|
blocks = []
|
||||||
if content_text: blocks.append({"type": "text", "text": content_text})
|
if content_text: blocks.append({"type": "text", "text": content_text})
|
||||||
@@ -226,9 +226,7 @@ def _parse_openai_sse(resp_lines, api_mode="chat_completions"):
|
|||||||
if tc.get("function", {}).get("name"): tc_buf[idx]["name"] = tc["function"]["name"]
|
if tc.get("function", {}).get("name"): tc_buf[idx]["name"] = tc["function"]["name"]
|
||||||
if tc.get("function", {}).get("arguments"): tc_buf[idx]["args"] += tc["function"]["arguments"]
|
if tc.get("function", {}).get("arguments"): tc_buf[idx]["args"] += tc["function"]["arguments"]
|
||||||
usage = evt.get("usage")
|
usage = evt.get("usage")
|
||||||
if usage:
|
if usage: _record_usage(usage, api_mode)
|
||||||
cached = (usage.get("prompt_tokens_details") or {}).get("cached_tokens", 0)
|
|
||||||
print(f"[Cache] input={usage.get('prompt_tokens',0)} cached={cached}")
|
|
||||||
blocks = []
|
blocks = []
|
||||||
if content_text: blocks.append({"type": "text", "text": content_text})
|
if content_text: blocks.append({"type": "text", "text": content_text})
|
||||||
for idx in sorted(tc_buf):
|
for idx in sorted(tc_buf):
|
||||||
@@ -238,13 +236,24 @@ def _parse_openai_sse(resp_lines, api_mode="chat_completions"):
|
|||||||
blocks.append({"type": "tool_use", "id": tc["id"], "name": tc["name"], "input": inp})
|
blocks.append({"type": "tool_use", "id": tc["id"], "name": tc["name"], "input": inp})
|
||||||
return blocks
|
return blocks
|
||||||
|
|
||||||
|
def _record_usage(usage, api_mode):
|
||||||
|
if not usage: return
|
||||||
|
if api_mode == 'responses':
|
||||||
|
cached = (usage.get("input_tokens_details") or {}).get("cached_tokens", 0)
|
||||||
|
inp = usage.get("input_tokens", 0)
|
||||||
|
print(f"[Cache] input={inp} cached={cached}")
|
||||||
|
elif api_mode == 'chat_completions':
|
||||||
|
cached = (usage.get("prompt_tokens_details") or {}).get("cached_tokens", 0)
|
||||||
|
inp = usage.get("prompt_tokens", 0)
|
||||||
|
print(f"[Cache] input={inp} cached={cached}")
|
||||||
|
elif api_mode == 'messages':
|
||||||
|
ci, cr, inp = usage.get("cache_creation_input_tokens", 0), usage.get("cache_read_input_tokens", 0), usage.get("input_tokens", 0)
|
||||||
|
print(f"[Cache] input={inp} creation={ci} read={cr}")
|
||||||
|
|
||||||
def _parse_openai_json(data, api_mode="chat_completions"):
|
def _parse_openai_json(data, api_mode="chat_completions"):
|
||||||
blocks = []
|
blocks = []
|
||||||
if api_mode == "responses":
|
if api_mode == "responses":
|
||||||
usage = data.get("usage", {})
|
_record_usage(data.get("usage") or {}, api_mode)
|
||||||
cached = (usage.get("input_tokens_details") or {}).get("cached_tokens", 0)
|
|
||||||
inp = usage.get("input_tokens", 0)
|
|
||||||
if inp: print(f"[Cache] input={inp} cached={cached}")
|
|
||||||
for item in (data.get("output") or []):
|
for item in (data.get("output") or []):
|
||||||
if item.get("type") == "message":
|
if item.get("type") == "message":
|
||||||
for p in (item.get("content") or []):
|
for p in (item.get("content") or []):
|
||||||
@@ -256,10 +265,7 @@ def _parse_openai_json(data, api_mode="chat_completions"):
|
|||||||
blocks.append({"type": "tool_use", "id": item.get("call_id", item.get("id", "")),
|
blocks.append({"type": "tool_use", "id": item.get("call_id", item.get("id", "")),
|
||||||
"name": item.get("name", ""), "input": args})
|
"name": item.get("name", ""), "input": args})
|
||||||
else:
|
else:
|
||||||
usage = data.get("usage") or {}
|
_record_usage(data.get("usage") or {}, api_mode)
|
||||||
if usage:
|
|
||||||
cached = (usage.get("prompt_tokens_details") or {}).get("cached_tokens", 0)
|
|
||||||
print(f"[Cache] input={usage.get('prompt_tokens', 0)} cached={cached}")
|
|
||||||
msg = (data.get("choices") or [{}])[0].get("message", {})
|
msg = (data.get("choices") or [{}])[0].get("message", {})
|
||||||
content = msg.get("content", "")
|
content = msg.get("content", "")
|
||||||
if content:
|
if content:
|
||||||
@@ -577,8 +583,7 @@ class NativeClaudeSession(BaseSession):
|
|||||||
if self.stream: return (yield from _parse_claude_sse(resp.iter_lines())) or []
|
if self.stream: return (yield from _parse_claude_sse(resp.iter_lines())) or []
|
||||||
else:
|
else:
|
||||||
data = resp.json(); content_blocks = data.get("content", [])
|
data = resp.json(); content_blocks = data.get("content", [])
|
||||||
usage = data.get("usage", {})
|
_record_usage(data.get("usage", {}), "messages")
|
||||||
print(f"[Cache] input={usage.get('input_tokens',0)} creation={usage.get('cache_creation_input_tokens',0)} read={usage.get('cache_read_input_tokens',0)}")
|
|
||||||
for b in content_blocks:
|
for b in content_blocks:
|
||||||
if b.get("type") == "text": yield b.get("text", "")
|
if b.get("type") == "text": yield b.get("text", "")
|
||||||
elif b.get("type") == "thinking": yield ""
|
elif b.get("type") == "thinking": yield ""
|
||||||
@@ -940,6 +945,3 @@ class NativeToolClient:
|
|||||||
if resp and hasattr(resp, 'tool_calls') and resp.tool_calls: self._pending_tool_ids = [tc.id for tc in resp.tool_calls]
|
if resp and hasattr(resp, 'tool_calls') and resp.tool_calls: self._pending_tool_ids = [tc.id for tc in resp.tool_calls]
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
try: import langfuse_tracing # opt-in observability; noop if langfuse_config not set in mykey
|
|
||||||
except Exception: pass
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user