1 line
13 KiB
JSON
1 line
13 KiB
JSON
{"repo": "chintaal/cd-el-repo", "files": {"tracer/backend/llm/analyst.py": {"c854638da04fa7ab23c30553c538bc39f38d9d8a": {"t": 1785746459.3828564, "n": 5117, "h": "58af0f981459388b", "c": "\"\"\"\nLLM orchestration for IR analysis — provider-agnostic via LiteLLM.\n\nDeepSeek is the showcased default (deepseek/deepseek-chat). Switch providers\npurely through env vars — see llm/providers.py and .env.example:\n\n LLM_MODEL=deepseek/deepseek-chat DEEPSEEK_API_KEY=sk-... (default)\n LLM_MODEL=deepseek/deepseek-reasoner DEEPSEEK_API_KEY=sk-... (+ chain-of-thought)\n LLM_MODEL=claude-sonnet-4-20250514 ANTHROPIC_API_KEY=sk-ant-...\n LLM_MODEL=gpt-4o OPENAI_API_KEY=sk-...\n LLM_MODEL=groq/llama-3.3-70b-versatile GROQ_API_KEY=gsk_...\n LLM_MODEL=ollama/llama3.2 LLM_BASE_URL=http://localhost:11434\n\nIf no provider is configured the caller falls back to pre-generated notes.\n\nStreaming helpers yield event dicts:\n {\"reasoning\": \"...\"} chain-of-thought delta (reasoning models only)\n {\"text\": \"...\"} answer delta\nso the SSE layer / UI can render thinking and answer separately.\n\"\"\"\nfrom __future__ import annotations\n\nimport os\nfrom typing import AsyncIterator\n\nimport litellm\n\nfrom llm import providers\nfrom llm.prompts.ir_analyst import (\n SYSTEM_PROMPT,\n MITRE_SUMMARY_PROMPT,\n ASK_SYSTEM_PROMPT,\n OPTIMIZE_SYSTEM_PROMPT,\n build_analysis_prompt,\n build_pattern_summary_prompt,\n build_ask_prompt,\n build_optimize_prompt,\n)\nfrom models.schemas import ExplainRequest\n\nlitellm.suppress_debug_info = True\n\nMAX_TOKENS = 1500\n\n\ndef _kwargs(max_tokens: int = MAX_TOKENS) -> dict:\n kw: dict = {\"max_tokens\": max_tokens}\n base = providers.base_url()\n if base:\n kw[\"api_base\"] = base\n return kw\n\n\nasync def _stream_events(messages: list[dict], max_tokens: int = MAX_TOKENS) -> AsyncIterator[dict]:\n \"\"\"Core streamer. Yields {'reasoning': str} and/or {'text': str} events.\"\"\"\n model = providers.current_model()\n response = await litellm.acompletion(\n model=model,\n messages=messages,\n stream=True,\n **_kwargs(max_tokens),\n )\n async for chunk in response:\n delta = chunk.choices[0].delta\n # DeepSeek-reasoner / o1 / thinking models expose reasoning separately\n reasoning = getattr(delta, \"reasoning_content\", None)\n if reasoning:\n yield {\"reasoning\": reasoning}\n text = getattr(delta, \"content\", None)\n if text:\n yield {\"text\": text}\n\n\ndef _analysis_messages(req: ExplainRequest) -> list[dict]:\n stages = req.stage_outputs\n prompt = build_analysis_prompt(\n construct_type=req.source_text,\n source_text=req.source_text,\n parse_tree_excerpt=stages.get(\"parse_tree\", \"\")[:800],\n fir_excerpt=stages.get(\"fir\", \"\")[:1000],\n hlfir_excerpt=stages.get(\"hlfir\", \"\")[:1000],\n llvm_ir_excerpt=stages.get(\"llvm_ir\", \"\")[:1000],\n selected_lines=str(req.source_line),\n lowering_context=req.lowering_context or \"\",\n )\n return [\n {\"role\": \"system\", \"content\": SYSTEM_PROMPT},\n {\"role\": \"user\", \"content\": prompt},\n ]\n\n\nasync def stream_analysis(req: ExplainRequest) -> AsyncIterator[dict]:\n \"\"\"Stream the lowering-chain explanation for a clicked source line.\"\"\"\n async for ev in _stream_events(_analysis_messages(req)):\n yield ev\n\n\nasync def stream_ask(\n construct_name: str,\n construct_type: str,\n source: str,\n stage_outputs: dict[str, str],\n question: str,\n) -> AsyncIterator[dict]:\n \"\"\"Stream a free-form answer about the current construct's IR.\"\"\"\n messages = [\n {\"role\": \"system\", \"content\": ASK_SYSTEM_PROMPT},\n {\"role\": \"user\", \"content\": build_ask_prompt(\n construct_name, construct_type, source, stage_outputs, question)},\n ]\n async for ev in _stream_events(messages, max_tokens=1200):\n yield ev\n\n\nasync def analyze_batch(req: ExplainRequest) -> str:\n \"\"\"Single blocking call; used by CLI and report generation.\"\"\"\n response = await litellm.acompletion(\n model=providers.current_model(),\n messages=_analysis_messages(req),\n **_kwargs(),\n )\n return response.choices[0].message.content\n\n\nasync def summarize_pattern(\n construct_name: str, key_ops: list[str], lowering_notes: str\n) -> str:\n prompt = build_pattern_summary_prompt(construct_name, key_ops, lowering_notes)\n response = await litellm.acompletion(\n model=providers.current_model(),\n messages=[\n {\"role\": \"system\", \"content\": MITRE_SUMMARY_PROMPT},\n {\"role\": \"user\", \"content\": prompt},\n ],\n **_kwargs(max_tokens=200),\n )\n return response.choices[0].message.content\n\n\nasync def optimize_rationale(construct_name: str, flags: dict, findings: list[str]) -> str:\n \"\"\"Short LLM rationale tying together the deterministic optimizer findings.\"\"\"\n response = await litellm.acompletion(\n model=providers.current_model(),\n messages=[\n {\"role\": \"system\", \"content\": OPTIMIZE_SYSTEM_PROMPT},\n {\"role\": \"user\", \"content\": build_optimize_prompt(construct_name, flags, findings)},\n ],\n **_kwargs(max_tokens=220),\n )\n return response.choices[0].message.content\n"}, "25c35d70a79d0e1ecb7b0ecda7be376fcbe82d9c": {"t": 1785764551.7299876, "n": 2723, "h": "b0932d4a56736139", "c": "\"\"\"\nClaude API orchestration for IR analysis.\n\nProvides both a streaming endpoint (SSE, for frontend typewriter effect)\nand a batch call (for CLI and report generation).\n\"\"\"\nfrom __future__ import annotations\n\nimport os\nfrom typing import AsyncIterator\n\nimport anthropic\n\nfrom llm.prompts.ir_analyst import (\n SYSTEM_PROMPT,\n MITRE_SUMMARY_PROMPT,\n build_analysis_prompt,\n build_pattern_summary_prompt,\n)\nfrom models.schemas import ExplainRequest\n\nMODEL = \"claude-sonnet-4-20250514\"\nMAX_TOKENS = 1500\n\n\ndef _client() -> anthropic.AsyncAnthropic:\n return anthropic.AsyncAnthropic(api_key=os.environ[\"ANTHROPIC_API_KEY\"])\n\n\nasync def stream_analysis(req: ExplainRequest) -> AsyncIterator[str]:\n \"\"\"Yields text chunks for SSE streaming to the frontend.\"\"\"\n stages = req.stage_outputs\n\n prompt = build_analysis_prompt(\n construct_type=req.source_text,\n source_text=req.source_text,\n parse_tree_excerpt=stages.get(\"parse_tree\", \"\")[:800],\n fir_excerpt=stages.get(\"fir\", \"\")[:1000],\n hlfir_excerpt=stages.get(\"hlfir\", \"\")[:1000],\n llvm_ir_excerpt=stages.get(\"llvm_ir\", \"\")[:1000],\n selected_lines=str(req.source_line),\n lowering_context=req.lowering_context or \"\",\n )\n\n async with _client().messages.stream(\n model=MODEL,\n max_tokens=MAX_TOKENS,\n system=SYSTEM_PROMPT,\n messages=[{\"role\": \"user\", \"content\": prompt}],\n ) as stream:\n async for text in stream.text_stream:\n yield text\n\n\nasync def analyze_batch(req: ExplainRequest) -> str:\n \"\"\"Single blocking call; used by CLI tool.\"\"\"\n stages = req.stage_outputs\n\n prompt = build_analysis_prompt(\n construct_type=req.source_text,\n source_text=req.source_text,\n parse_tree_excerpt=stages.get(\"parse_tree\", \"\")[:800],\n fir_excerpt=stages.get(\"fir\", \"\")[:1000],\n hlfir_excerpt=stages.get(\"hlfir\", \"\")[:1000],\n llvm_ir_excerpt=stages.get(\"llvm_ir\", \"\")[:1000],\n selected_lines=str(req.source_line),\n lowering_context=req.lowering_context or \"\",\n )\n\n result = await _client().messages.create(\n model=MODEL,\n max_tokens=MAX_TOKENS,\n system=SYSTEM_PROMPT,\n messages=[{\"role\": \"user\", \"content\": prompt}],\n )\n return result.content[0].text\n\n\nasync def summarize_pattern(construct_name: str, key_ops: list[str], lowering_notes: str) -> str:\n prompt = build_pattern_summary_prompt(construct_name, key_ops, lowering_notes)\n result = await _client().messages.create(\n model=MODEL,\n max_tokens=200,\n system=MITRE_SUMMARY_PROMPT,\n messages=[{\"role\": \"user\", \"content\": prompt}],\n )\n return result.content[0].text\n"}}, ".env": {"c854638da04fa7ab23c30553c538bc39f38d9d8a": {"t": 1785747112.757654, "n": 1623, "h": "87575d8868c3aaea", "c": "# ── LLM configuration (optional — app works fully without it) ─────────────────\n#\n# fpc-trace talks to any provider through LiteLLM. Pick a model with LLM_MODEL\n# and supply that provider's API key. Without a key, AI features fall back to\n# deterministic pre-generated lowering notes (the app stays fully usable).\n#\n# ┌─ DeepSeek (default & recommended) ──────────────────────────────────────────\n# │ Cheap, strong at code/compilers, OpenAI-compatible. Get a key at\n# │ https://platform.deepseek.com → it's already the built-in default model.\nLLM_MODEL=deepseek/deepseek-chat\nDEEPSEEK_API_KEY=sk-e1606130746449e7b62030be9b245800\n\n# │ Want the model to show its chain-of-thought? Use the reasoner variant —\n# │ fpc-trace streams the reasoning into a separate collapsible \"thinking\" pane:\n# LLM_MODEL=deepseek/deepseek-reasoner\n# └─────────────────────────────────────────────────────────────────────────────\n#\n# Anthropic (Claude)\n# LLM_MODEL=claude-sonnet-4-20250514\n# ANTHROPIC_API_KEY=sk-ant-...\n#\n# OpenAI\n# LLM_MODEL=gpt-4o\n# OPENAI_API_KEY=sk-...\n#\n# Groq (fast, free tier)\n# LLM_MODEL=groq/llama-3.3-70b-versatile\n# GROQ_API_KEY=gsk_...\n#\n# Google Gemini\n# LLM_MODEL=gemini/gemini-2.0-flash\n# GEMINI_API_KEY=AIza...\n#\n# Local Ollama (no key needed)\n# LLM_MODEL=ollama/llama3.2\n# LLM_BASE_URL=http://localhost:11434\n\n# LLM_BASE_URL= # custom endpoint, e.g. for Ollama / LM Studio / vLLM\n\n# ── Frontend deployment (Vercel + separate backend) ───────────────────────────\n# Leave unset for local dev or Docker (proxy handles /api routing).\n# VITE_API_URL=https://fpc-trace.onrender.com\n"}}, ".env.example": {"c854638da04fa7ab23c30553c538bc39f38d9d8a": {"t": 1785747125.6613805, "n": 1623, "h": "87575d8868c3aaea", "c": "# ── LLM configuration (optional — app works fully without it) ─────────────────\n#\n# fpc-trace talks to any provider through LiteLLM. Pick a model with LLM_MODEL\n# and supply that provider's API key. Without a key, AI features fall back to\n# deterministic pre-generated lowering notes (the app stays fully usable).\n#\n# ┌─ DeepSeek (default & recommended) ──────────────────────────────────────────\n# │ Cheap, strong at code/compilers, OpenAI-compatible. Get a key at\n# │ https://platform.deepseek.com → it's already the built-in default model.\nLLM_MODEL=deepseek/deepseek-chat\nDEEPSEEK_API_KEY=sk-e1606130746449e7b62030be9b245800\n\n# │ Want the model to show its chain-of-thought? Use the reasoner variant —\n# │ fpc-trace streams the reasoning into a separate collapsible \"thinking\" pane:\n# LLM_MODEL=deepseek/deepseek-reasoner\n# └─────────────────────────────────────────────────────────────────────────────\n#\n# Anthropic (Claude)\n# LLM_MODEL=claude-sonnet-4-20250514\n# ANTHROPIC_API_KEY=sk-ant-...\n#\n# OpenAI\n# LLM_MODEL=gpt-4o\n# OPENAI_API_KEY=sk-...\n#\n# Groq (fast, free tier)\n# LLM_MODEL=groq/llama-3.3-70b-versatile\n# GROQ_API_KEY=gsk_...\n#\n# Google Gemini\n# LLM_MODEL=gemini/gemini-2.0-flash\n# GEMINI_API_KEY=AIza...\n#\n# Local Ollama (no key needed)\n# LLM_MODEL=ollama/llama3.2\n# LLM_BASE_URL=http://localhost:11434\n\n# LLM_BASE_URL= # custom endpoint, e.g. for Ollama / LM Studio / vLLM\n\n# ── Frontend deployment (Vercel + separate backend) ───────────────────────────\n# Leave unset for local dev or Docker (proxy handles /api routing).\n# VITE_API_URL=https://fpc-trace.onrender.com\n"}}}} |