Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/alanxbitcoin_impetuschain.json
T

1 line
20 KiB
JSON
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "alanxbitcoin/impetuschain", "files": {"src/impetus_chain/ai_gateway.py": {"e759a7ee026a35253b5373dacd00bd3b95f277d1": {"t": 1785746485.4688468, "n": 18655, "h": "db570c0cfbc1851c", "c": "import json\nimport os\nfrom pathlib import Path\nimport urllib.error\nimport urllib.parse\nimport urllib.request\nfrom dataclasses import dataclass\nfrom typing import Any, Callable\n\n\n@dataclass(frozen=True)\nclass ProviderSpec:\n provider_id: str\n label: str\n kind: str\n model: str\n api_key_env: str\n base_url: str = \"\"\n\n\nPROVIDERS: dict[str, ProviderSpec] = {\n \"openai\": ProviderSpec(\n provider_id=\"openai\",\n label=\"OpenAI GPT-5.5 (娣卞害鍒嗘瀽)\",\n kind=\"openai_responses\",\n model=\"gpt-5.5\",\n api_key_env=\"OPENAI_API_KEY\",\n base_url=\"https://api.openai.com/v1/responses\",\n ),\n \"openai_gpt54\": ProviderSpec(\n provider_id=\"openai_gpt54\",\n label=\"OpenAI GPT-5.4 (骞宠 閫熷害/璐ㄩ噺)\",\n kind=\"openai_responses\",\n model=\"gpt-5.4\",\n api_key_env=\"OPENAI_API_KEY\",\n base_url=\"https://api.openai.com/v1/responses\",\n ),\n \"openai_gpt54mini\": ProviderSpec(\n provider_id=\"openai_gpt54mini\",\n label=\"OpenAI GPT-5.4-mini (楂橀/浣庢垚鏈?\",\n kind=\"openai_responses\",\n model=\"gpt-5.4-mini\",\n api_key_env=\"OPENAI_API_KEY\",\n base_url=\"https://api.openai.com/v1/responses\",\n ),\n \"anthropic\": ProviderSpec(\n provider_id=\"anthropic\",\n label=\"Anthropic\",\n kind=\"anthropic_messages\",\n model=\"claude-3-5-sonnet-20241022\",\n api_key_env=\"ANTHROPIC_API_KEY\",\n base_url=\"https://api.anthropic.com/v1/messages\",\n ),\n \"gemini\": ProviderSpec(\n provider_id=\"gemini\",\n label=\"Google Gemini\",\n kind=\"gemini_generate_content\",\n model=\"gemini-1.5-pro\",\n api_key_env=\"GEMINI_API_KEY\",\n ),\n \"deepseek\": ProviderSpec(\n provider_id=\"deepseek\",\n label=\"DeepSeek\",\n kind=\"openai_compatible\",\n model=\"deepseek-chat\",\n api_key_env=\"DEEPSEEK_API_KEY\",\n base_url=\"https://api.deepseek.com/chat/completions\",\n ),\n \"qwen\": ProviderSpec(\n provider_id=\"qwen\",\n label=\"Qwen (DashScope)\",\n kind=\"openai_compatible\",\n model=\"qwen-plus\",\n api_key_env=\"DASHSCOPE_API_KEY\",\n base_url=\"https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions\",\n ),\n}\n\n\nMODULE_DIR = Path(__file__).resolve().parent\nPROMPTS_DIR = MODULE_DIR / 'prompts'\nFULL_PROMPT_PATH = PROMPTS_DIR / 'node_analysis_full_prompt.txt'\nSTEP_PROMPT_PATH = PROMPTS_DIR / 'node_analysis_step_prompt.txt'\n\ndef provider_status() -> list[dict[str, Any]]:\n rows: list[dict[str, Any]] = []\n for provider in PROVIDERS.values():\n rows.append(\n {\n \"provider_id\": provider.provider_id,\n \"label\": provider.label,\n \"enabled\": bool(os.getenv(provider.api_key_env, \"\").strip()),\n \"api_key_env\": provider.api_key_env,\n }\n )\n return rows\n\n\ndef _mask_headers(headers: dict[str, str]) -> dict[str, str]:\n masked: dict[str, str] = {}\n for key, value in headers.items():\n if key.lower() in {\"authorization\", \"x-api-key\"}:\n masked[key] = \"***\"\n else:\n masked[key] = value\n return masked\n\n\ndef _http_post_json(\n url: str,\n payload: dict,\n headers: dict[str, str],\n progress: Callable[[str], None] | None = None,\n) -> dict:\n raw = json.dumps(payload).encode(\"utf-8\")\n if progress:\n progress(\n \"API request\\n\"\n f\"url: {url}\\n\"\n f\"headers: {json.dumps(_mask_headers(headers), ensure_ascii=False)}\\n\"\n f\"payload: {json.dumps(payload, ensure_ascii=False)}\"\n )\n req = urllib.request.Request(url=url, data=raw, method=\"POST\")\n req.add_header(\"Content-Type\", \"application/json\")\n for key, value in headers.items():\n req.add_header(key, value)\n try:\n with urllib.request.urlopen(req, timeout=60) as resp:\n body = resp.read().decode(\"utf-8\")\n if progress:\n progress(f\"API response\\nstatus: {getattr(resp, 'status', 'unknown')}\\nbody: {body}\")\n return json.loads(body)\n except urllib.error.HTTPError as exc:\n err_body = exc.read().decode(\"utf-8\", errors=\"replace\")\n if progress:\n progress(f\"API HTTP error {exc.code}\\nbody: {err_body}\")\n raise RuntimeError(f\"AI HTTP error {exc.code}: {err_body}\") from exc\n except urllib.error.URLError as exc:\n if progress:\n progress(f\"API network error: {exc.reason}\")\n raise RuntimeError(f\"AI network error: {exc.reason}\") from exc\n\n\ndef _read_prompt_template(path: Path, fallback: str) -> str:\n if path.exists():\n return path.read_text(encoding=\"utf-8\")\n return fallback\n\n\ndef _render_prompt(template: str, values: dict[str, str]) -> str:\n out = template\n for key, value in values.items():\n out = out.replace(key, value)\n return out\n\n\ndef _build_prompt(\n project: str,\n node: str,\n core_intro: str,\n strategy_text: str,\n prompt_script_text: str,\n) -> str:\n fallback = (\n \"You are a financial research assistant.\\n\\n\"\n \"Project Context:\\n\"\n \"- Project: __PROJECT__\\n\"\n \"- Selected Node: __NODE__\\n\"\n \"- Core Impetus Intro: __CORE_INTRO__\\n\\n\"\n \"You MUST follow these 5 analysis steps and output machine-readable keys first.\\n\\n\"\n \"Analysis Strategy File (authoritative project core):\\n\"\n \"__STRATEGY_TEXT__\\n\\n\"\n \"AI Prompt Script File (communication protocol):\\n\"\n \"__PROMPT_SCRIPT_TEXT__\\n\"\n )\n template = _read_prompt_template(FULL_PROMPT_PATH, fallback)\n return _render_prompt(\n template,\n {\n \"__PROJECT__\": project,\n \"__NODE__\": node,\n \"__CORE_INTRO__\": core_intro or \"N/A\",\n \"__STRATEGY_TEXT__\": strategy_text or \"[EMPTY]\",\n \"__PROMPT_SCRIPT_TEXT__\": prompt_script_text or \"[EMPTY]\",\n },\n )\n\n\ndef _build_step_prompt(\n project: str,\n node: str,\n core_intro: str,\n strategy_text: str,\n prompt_script_text: str,\n step_name: str,\n step_instruction: str,\n required_keys: list[str],\n) -> str:\n key_lines = \", \".join(required_keys)\n fallback = (\n \"You are a financial research assistant.\\n\\n\"\n \"Project Context:\\n\"\n \"- Project: __PROJECT__\\n\"\n \"- Selected Node: __NODE__\\n\"\n \"- Core Impetus Intro: __CORE_INTRO__\\n\\n\"\n \"Current Step: __STEP_NAME__\\n\"\n \"Instruction: __STEP_INSTRUCTION__\\n\\n\"\n \"Output must be STRICT JSON only (no markdown, no prose, no code fences).\\n\"\n \"Use exactly one JSON object with these keys:\\n\"\n \"__REQUIRED_KEYS__\\n\\n\"\n \"Analysis Strategy File (authoritative project core):\\n\"\n \"__STRATEGY_TEXT__\\n\\n\"\n \"AI Prompt Script File (communication protocol):\\n\"\n \"__PROMPT_SCRIPT_TEXT__\\n\\n\"\n \"If uncertain, still return valid JSON with best estimate and concise values.\"\n )\n template = _read_prompt_template(STEP_PROMPT_PATH, fallback)\n return _render_prompt(\n template,\n {\n \"__PROJECT__\": project,\n \"__NODE__\": node,\n \"__CORE_INTRO__\": core_intro or \"N/A\",\n \"__STEP_NAME__\": step_name,\n \"__STEP_INSTRUCTION__\": step_instruction,\n \"__REQUIRED_KEYS__\": key_lines,\n \"__STRATEGY_TEXT__\": strategy_text or \"[EMPTY]\",\n \"__PROMPT_SCRIPT_TEXT__\": prompt_script_text or \"[EMPTY]\",\n },\n )\n\n\ndef _extract_json_object(text: str) -> dict[str, Any]:\n text = text.strip()\n if not text:\n return {}\n try:\n parsed = json.loads(text)\n if isinstance(parsed, dict):\n return parsed\n except Exception:\n pass\n start = text.find(\"{\")\n end = text.rfind(\"}\")\n if start >= 0 and end > start:\n chunk = text[start : end + 1]\n try:\n parsed = json.loads(chunk)\n if isinstance(parsed, dict):\n return parsed\n except Exception:\n return {}\n return {}\n\n\ndef _normalize_text_value(value: Any, max_len: int = 240) -> str:\n text = str(value or \"\").strip()\n text = \" \".join(text.split())\n return text[:max_len]\n\n\ndef _normalize_impetus_type(value: Any) -> str:\n raw = _normalize_text_value(value, max_len=64).lower()\n allowed = {\n \"natural_disaster\",\n \"political_event\",\n \"social_event\",\n \"technology_progress\",\n }\n return raw if raw in allowed else \"social_event\"\n\n\ndef _normalize_list(value: Any, max_items: int = 8, max_len: int = 80) -> list[str]:\n if isinstance(value, list):\n items = [str(v).strip() for v in value]\n else:\n items = [s.strip() for s in str(value or \"\").split(\",\")]\n out: list[str] = []\n seen: set[str] = set()\n for item in items:\n if not item:\n continue\n norm = \" \".join(item.split())[:max_len]\n key = norm.casefold()\n if not norm or key in seen:\n continue\n seen.add(key)\n out.append(norm)\n if len(out) >= max_items:\n break\n return out\n\n\ndef _call_openai_compatible(\n spec: ProviderSpec, api_key: str, prompt: str, progress: Callable[[str], None] | None = None\n) -> str:\n payload = {\n \"model\": spec.model,\n \"messages\": [\n {\"role\": \"system\", \"content\": \"You are a concise financial analyst assistant.\"},\n {\"role\": \"user\", \"content\": prompt},\n ],\n \"temperature\": 0.2,\n }\n body = _http_post_json(\n url=spec.base_url,\n payload=payload,\n headers={\"Authorization\": f\"Bearer {api_key}\"},\n progress=progress,\n )\n return str(body[\"choices\"][0][\"message\"][\"content\"]).strip()\n\n\ndef _extract_openai_response_text(body: dict) -> str:\n output_text = str(body.get(\"output_text\", \"\")).strip()\n if output_text:\n return output_text\n chunks: list[str] = []\n for item in body.get(\"output\", []):\n for content in item.get(\"content\", []):\n text = str(content.get(\"text\", \"\")).strip()\n if text:\n chunks.append(text)\n return \"\\n\".join(chunks).strip()\n\n\ndef _call_openai_responses(\n spec: ProviderSpec, api_key: str, prompt: str, progress: Callable[[str], None] | None = None\n) -> str:\n payload = {\n \"model\": spec.model,\n \"reasoning\": {\"effort\": \"high\"},\n \"tools\": [{\"type\": \"web_search\"}],\n \"input\": prompt,\n }\n body = _http_post_json(\n url=spec.base_url,\n payload=payload,\n headers={\"Authorization\": f\"Bearer {api_key}\"},\n progress=progress,\n )\n return _extract_openai_response_text(body)\n\n\ndef _call_anthropic(\n spec: ProviderSpec, api_key: str, prompt: str, progress: Callable[[str], None] | None = None\n) -> str:\n payload = {\n \"model\": spec.model,\n \"max_tokens\": 1024,\n \"messages\": [{\"role\": \"user\", \"content\": prompt}],\n }\n body = _http_post_json(\n url=spec.base_url,\n payload=payload,\n headers={\"x-api-key\": api_key, \"anthropic-version\": \"2023-06-01\"},\n progress=progress,\n )\n parts = body.get(\"content\", [])\n texts = [str(item.get(\"text\", \"\")) for item in parts if item.get(\"type\") == \"text\"]\n return \"\\n\".join(t for t in texts if t).strip()\n\n\ndef _call_gemini(\n spec: ProviderSpec, api_key: str, prompt: str, progress: Callable[[str], None] | None = None\n) -> str:\n encoded_model = urllib.parse.quote(spec.model, safe=\"\")\n key = urllib.parse.quote(api_key, safe=\"\")\n url = (\n \"https://generativelanguage.googleapis.com/v1beta/models/\"\n f\"{encoded_model}:generateContent?key={key}\"\n )\n payload = {\"contents\": [{\"parts\": [{\"text\": prompt}]}]}\n body = _http_post_json(url=url, payload=payload, headers={}, progress=progress)\n candidates = body.get(\"candidates\", [])\n if not candidates:\n return \"\"\n parts = candidates[0].get(\"content\", {}).get(\"parts\", [])\n texts = [str(item.get(\"text\", \"\")) for item in parts]\n return \"\\n\".join(t for t in texts if t).strip()\n\n\ndef analyze_node_with_ai(\n provider_id: str,\n project: str,\n node: str,\n core_intro: str = \"\",\n strategy_text: str = \"\",\n prompt_script_text: str = \"\",\n api_key_override: str = \"\",\n progress: Callable[[str], None] | None = None,\n) -> str:\n spec = PROVIDERS.get(provider_id)\n if spec is None:\n raise ValueError(f\"Unsupported AI provider: {provider_id}\")\n\n api_key = api_key_override.strip() or os.getenv(spec.api_key_env, \"\").strip()\n if not api_key:\n raise ValueError(f\"Missing API key: set env var {spec.api_key_env}\")\n\n prompt = _build_prompt(\n project=project,\n node=node,\n core_intro=core_intro,\n strategy_text=strategy_text,\n prompt_script_text=prompt_script_text,\n )\n if progress:\n progress(f\"Prompt\\n{prompt}\")\n if spec.kind == \"openai_responses\":\n return _call_openai_responses(spec=spec, api_key=api_key, prompt=prompt, progress=progress)\n if spec.kind == \"openai_compatible\":\n return _call_openai_compatible(spec=spec, api_key=api_key, prompt=prompt, progress=progress)\n if spec.kind == \"anthropic_messages\":\n return _call_anthropic(spec=spec, api_key=api_key, prompt=prompt, progress=progress)\n if spec.kind == \"gemini_generate_content\":\n return _call_gemini(spec=spec, api_key=api_key, prompt=prompt, progress=progress)\n raise ValueError(f\"Unsupported provider kind: {spec.kind}\")\n\n\ndef analyze_node_with_ai_five_steps(\n provider_id: str,\n project: str,\n node: str,\n core_intro: str = \"\",\n strategy_text: str = \"\",\n prompt_script_text: str = \"\",\n api_key_override: str = \"\",\n progress: Callable[[str], None] | None = None,\n) -> str:\n spec = PROVIDERS.get(provider_id)\n if spec is None:\n raise ValueError(f\"Unsupported AI provider: {provider_id}\")\n\n api_key = api_key_override.strip() or os.getenv(spec.api_key_env, \"\").strip()\n if not api_key:\n raise ValueError(f\"Missing API key: set env var {spec.api_key_env}\")\n\n steps = [\n (\n \"Step-1\",\n \"Classify impetus type from exactly one option: natural_disaster/political_event/social_event/technology_progress.\",\n [\"impetus_type\"],\n ),\n (\n \"Step-2\",\n \"Estimate expected duration and force evolution pattern.\",\n [\"duration_estimate\", \"force_pattern\"],\n ),\n (\n \"Step-3\",\n \"Estimate impact magnitude using global financial market cap change (USD range).\",\n [\"impact_market_cap_change_range_usd\"],\n ),\n (\n \"Step-4\",\n \"Identify directly tradable speculation targets (commodities/equities/bonds/currencies/indices).\",\n [\"direct_spec_targets\"],\n ),\n (\n \"Step-5\",\n \"Generate next-level expansion child nodes (3-8 short noun phrases) as JSON array.\",\n [\"next_level_nodes_json\"],\n ),\n ]\n\n structured: dict[str, Any] = {\n \"impetus_type\": \"social_event\",\n \"duration_estimate\": \"\",\n \"force_pattern\": \"\",\n \"impact_market_cap_change_range_usd\": \"\",\n \"direct_spec_targets\": [],\n \"next_level_nodes_json\": [],\n }\n\n for idx, (step_name, instruction, keys) in enumerate(steps, start=1):\n if progress:\n progress(f\"{step_name}: 发起第 {idx}/5 次 API 调用\")\n step_prompt = _build_step_prompt(\n project=project,\n node=node,\n core_intro=core_intro,\n strategy_text=strategy_text,\n prompt_script_text=prompt_script_text,\n step_name=step_name,\n step_instruction=instruction,\n required_keys=keys,\n )\n if progress:\n progress(f\"{step_name} Prompt\\n{step_prompt}\")\n if spec.kind == \"openai_responses\":\n step_text = _call_openai_responses(\n spec=spec, api_key=api_key, prompt=step_prompt, progress=progress\n )\n elif spec.kind == \"openai_compatible\":\n step_text = _call_openai_compatible(\n spec=spec, api_key=api_key, prompt=step_prompt, progress=progress\n )\n elif spec.kind == \"anthropic_messages\":\n step_text = _call_anthropic(\n spec=spec, api_key=api_key, prompt=step_prompt, progress=progress\n )\n elif spec.kind == \"gemini_generate_content\":\n step_text = _call_gemini(\n spec=spec, api_key=api_key, prompt=step_prompt, progress=progress\n )\n else:\n raise ValueError(f\"Unsupported provider kind: {spec.kind}\")\n\n if progress:\n progress(f\"{step_name}: 完成第 {idx}/5 次 API 调用\")\n data = _extract_json_object(step_text)\n if not data:\n if progress:\n progress(f\"{step_name}: 未解析到 JSON,将使用默认/空值\")\n data = {}\n\n if \"impetus_type\" in keys:\n structured[\"impetus_type\"] = _normalize_impetus_type(data.get(\"impetus_type\"))\n if \"duration_estimate\" in keys:\n structured[\"duration_estimate\"] = _normalize_text_value(\n data.get(\"duration_estimate\"), 120\n )\n if \"force_pattern\" in keys:\n structured[\"force_pattern\"] = _normalize_text_value(data.get(\"force_pattern\"), 120)\n if \"impact_market_cap_change_range_usd\" in keys:\n structured[\"impact_market_cap_change_range_usd\"] = _normalize_text_value(\n data.get(\"impact_market_cap_change_range_usd\"), 160\n )\n if \"direct_spec_targets\" in keys:\n structured[\"direct_spec_targets\"] = _normalize_list(\n data.get(\"direct_spec_targets\"), max_items=12, max_len=64\n )\n if \"next_level_nodes_json\" in keys:\n structured[\"next_level_nodes_json\"] = _normalize_list(\n data.get(\"next_level_nodes_json\"), max_items=8, max_len=80\n )\n\n return (\n f\"impetus_type: {structured['impetus_type']}\\n\"\n f\"duration_estimate: {structured['duration_estimate'] or 'unspecified'}\\n\"\n f\"force_pattern: {structured['force_pattern'] or 'unspecified'}\\n\"\n f\"impact_market_cap_change_range_usd: {structured['impact_market_cap_change_range_usd'] or 'unspecified'}\\n\"\n f\"direct_spec_targets: {', '.join(structured['direct_spec_targets']) or 'unspecified'}\\n\"\n f\"next_level_nodes_json: {json.dumps(structured['next_level_nodes_json'], ensure_ascii=False)}\\n\"\n f\"structured_json: {json.dumps(structured, ensure_ascii=False)}\"\n )\n\n\n"}}}}