# legacy_llmcore.py — llmcore非native三件套(待淘汰), 单向依赖llmcore(禁from-import), 尾部回挂名 # 删除条件: [Legacy]日志零引用后, 删本文件+llmcore尾3行+resolve中non-native分支 import os, json, re import llmcore class ClaudeSession(llmcore.BaseSession): def raw_ask(self, messages): messages = llmcore._fix_messages(messages) if self.max_tokens is None: self.max_tokens = 8192 headers = {"x-api-key": self.api_key, "Content-Type": "application/json", "anthropic-version": "2023-06-01", "anthropic-beta": "prompt-caching-2024-07-31"} payload = {"model": self.model, "messages": messages, "max_tokens": self.max_tokens, "stream": self.stream} if self.temperature != 1: payload["temperature"] = self.temperature self._apply_claude_thinking(payload) if self.system: payload["system"] = [{"type": "text", "text": self.system, "cache_control": {"type": "persistent"}}] url = llmcore.auto_make_url(self.api_base, "messages") parse_fn = (lambda r: llmcore._parse_claude_sse(r.iter_lines())) if self.stream else (lambda r: llmcore._parse_claude_json(r.json())) return (yield from llmcore._stream_with_retry(self, url, headers, payload, parse_fn)) def make_messages(self, raw_list): msgs = llmcore._drop_unsigned_thinking([{"role": m['role'], "content": list(m['content'])} for m in raw_list]) user_idxs = [i for i, m in enumerate(msgs) if m['role'] == 'user'] for idx in user_idxs[-2:]: msgs[idx]["content"][-1] = dict(msgs[idx]["content"][-1], cache_control={"type": "ephemeral"}) return msgs class LLMSession(llmcore.BaseSession): def raw_ask(self, messages): return (yield from llmcore._openai_stream(self, messages)) def make_messages(self, raw_list): return llmcore._msgs_claude2oai(llmcore._fix_messages(raw_list)) class ToolClient: def __init__(self, backend, auto_save_tokens=True): self.backend = backend self.auto_save_tokens = auto_save_tokens self.last_tools = '' self.name = self.backend.name self.total_cd_tokens = 0 self.log_path = None def chat(self, messages, tools=None): tools = json.loads(json.dumps(tools, ensure_ascii=False)) if tools else tools for t in tools or []: f = t.get('function', {}) if f.get('name') == 'file_write': props = f.get('parameters', {}).get('properties', {}) props.pop('content', None) extra = '. Content must be placed in tags in reply body, not in args' if extra not in f.get('description', ''): f['description'] = f.get('description', '') + extra break full_prompt = self._build_protocol_prompt(messages, tools) print("Full prompt length:", len(full_prompt), 'chars') gen = self.backend.ask(full_prompt) llmcore._write_llm_log('Prompt', full_prompt, self.log_path) raw_text = '' for chunk in gen: raw_text += chunk; yield chunk llmcore._write_llm_log('Response', raw_text, self.log_path, model=self.backend.model) return self._parse_mixed_response(raw_text) def _prepare_tool_instruction(self, tools): tool_instruction = "" if not tools: return tool_instruction tools_json = json.dumps(tools, ensure_ascii=False, separators=(',', ':')) _en = os.environ.get('GA_LANG') == 'en' if _en: tool_instruction = f""" ### Interaction Protocol (must follow strictly, always in effect) Follow these steps to think and act: 1. **Think**: Analyze the current situation and strategy inside `` tags. 2. **Summarize**: Output a minimal one-line (<30 words) physical snapshot in ``: new info from last tool result + current tool call intent. This goes into long-term working memory. Must contain real information, no filler. 3. **Act**: If you need to call tools, output one or more ** blocks** after your reply, then stop. """ else: tool_instruction = f""" ### 交互协议 (必须严格遵守,持续有效) 请按照以下步骤思考并行动: 1. **思考**: 在 `` 标签中先进行思考,分析现状和策略。 2. **总结**: 在 `` 中输出*极为简短*的高度概括的单行(<30字)物理快照,包括上次工具调用结果产生的新信息+本次工具调用意图。此内容将进入长期工作记忆,记录关键信息,严禁输出无实际信息增量的描述。 3. **行动**: 如需调用工具,请在回复正文之后输出一个(或多个)**块**,然后结束。 """ tool_instruction += f'\nFormat: ```{{"name": "tool_name", "arguments": {{...}}}}```\n\n### Tools (mounted, always in effect):\n{tools_json}\n' if self.auto_save_tokens and self.last_tools == tools_json: tool_instruction = "\n### Tools: still active, **ready to call**. Protocol unchanged.\n" if _en else "\n### 工具库状态:持续有效(code_run/file_read等),**可正常调用**。调用协议沿用。\n" else: self.total_cd_tokens = 0 self.last_tools = tools_json return tool_instruction def _build_protocol_prompt(self, messages, tools): system_content = next((m['content'] for m in messages if m['role'].lower() == 'system'), "") history_msgs = [m for m in messages if m['role'].lower() != 'system'] tool_instruction = self._prepare_tool_instruction(tools) system = ""; user = "" if system_content: system += f"{system_content}\n" system += f"{tool_instruction}" for m in history_msgs: role = "USER" if m['role'] == 'user' else "ASSISTANT" user += f"=== {role} ===\n" for tr in m.get('tool_results', []): user += f'{tr["content"]}\n' user += str(m['content']) + "\n" self.total_cd_tokens += len(user) // 3 if self.total_cd_tokens > 9000: self.last_tools = '' user += "=== ASSISTANT ===\n" return system + user def _parse_mixed_response(self, text): remaining_text = text; thinking = '' think_match = re.search(r"(.*?)", text, re.DOTALL) if think_match: thinking = think_match.group(1).strip() remaining_text = re.sub(r"(.*?)", "", remaining_text, flags=re.DOTALL) tool_calls, remaining_text = llmcore._parse_text_tool_calls(remaining_text) if not tool_calls: json_strs = []; errors = [] if '' in remaining_text: weaktoolstr = remaining_text.split('')[-1].strip().strip('><') json_str = weaktoolstr if weaktoolstr.endswith('}') else '' if json_str == '' and '```' in weaktoolstr and weaktoolstr.split('```')[0].strip().endswith('}'): json_str = weaktoolstr.split('```')[0].strip() if json_str: json_strs.append(json_str) remaining_text = remaining_text.replace(''+weaktoolstr, "") elif '"name":' in remaining_text and '"arguments":' in remaining_text: json_match = re.search(r'\{.*"name":.*\}', remaining_text, re.DOTALL) if json_match: json_strs.append(json_match.group(0).strip()) remaining_text = remaining_text.replace(json_match.group(0), "").strip() for json_str in json_strs: try: data = llmcore.tryparse(json_str) func_name = data.get('name') or data.get('function') or data.get('tool') args = data.get('arguments') or data.get('args') or data.get('params') or data.get('parameters') if args is None: args = data if func_name: tool_calls.append(llmcore.MockToolCall(func_name, args)) except json.JSONDecodeError: errors.append(f'Failed to parse tool_use JSON: {json_str[:200]}') self.last_tools = '' except: pass if not tool_calls: for e in errors: print(f"[Warn] {e}"); tool_calls.append(llmcore.MockToolCall('bad_json', {'msg': e})) return llmcore.MockResponse(thinking, remaining_text.strip(), tool_calls, text) # 尾挂名: 回挂llmcore(谁后初始化完谁挂) llmcore.ClaudeSession = ClaudeSession llmcore.LLMSession = LLMSession llmcore.ToolClient = ToolClient