Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
13 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ jobs:
python-version: "3.11"
cache: "pip"
cache-dependency-path: |
libs/openant-core/requirements.txt
libs/openant-core/pyproject.toml

- name: Set up Node.js
Expand All @@ -35,7 +34,7 @@ jobs:

- name: Install Python dependencies
working-directory: libs/openant-core
run: pip install -r requirements.txt && pip install ".[dev]"
run: pip install -e ".[dev]"

- name: Cache JS parser node_modules
id: cache-node-modules
Expand All @@ -51,7 +50,7 @@ jobs:

- name: Run Python and parser tests
working-directory: libs/openant-core
run: python -m pytest tests/test_token_tracker.py tests/test_parser_adapter.py tests/test_python_parser.py tests/test_js_parser.py -v
run: python -m pytest tests/test_token_tracker.py tests/test_parser_adapter.py tests/test_python_parser.py tests/test_js_parser.py tests/test_declared_dependencies.py -v

go-tests:
name: Go build + integration (${{ matrix.os }})
Expand Down Expand Up @@ -79,7 +78,6 @@ jobs:
python-version: "3.11"
cache: "pip"
cache-dependency-path: |
libs/openant-core/requirements.txt
libs/openant-core/pyproject.toml

- name: Set up Node.js
Expand Down Expand Up @@ -115,7 +113,7 @@ jobs:

- name: Install Python dependencies
working-directory: libs/openant-core
run: pip install -r requirements.txt && pip install ".[dev]"
run: pip install -e ".[dev]"

- name: Cache JS parser node_modules
id: cache-node-modules
Expand Down
2 changes: 1 addition & 1 deletion libs/openant-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ git clone https://github.com/your-org/openant.git
cd openant

# Install Python dependencies
pip install -r requirements.txt
pip install -e .

# Set API key
echo "ANTHROPIC_API_KEY=your-key-here" > .env
Expand Down
27 changes: 15 additions & 12 deletions libs/openant-core/context/application_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,17 @@
from pathlib import Path
from typing import Any

from anthropic import Anthropic
from dotenv import load_dotenv

# Ensure libs/openant-core is on sys.path so `utilities.*` imports resolve
# regardless of how this module is loaded.
_OPENANT_CORE_ROOT = str(Path(__file__).parent.parent)
if _OPENANT_CORE_ROOT not in sys.path:
sys.path.insert(0, _OPENANT_CORE_ROOT)

from utilities.model_config import MODEL_AUXILIARY # noqa: E402
from utilities.llm_client import AnthropicClient # noqa: E402

# Load environment variables
load_dotenv()

Expand Down Expand Up @@ -462,7 +470,7 @@ def _build_type_descriptions() -> str:

def generate_application_context(
repo_path: Path,
model: str = "claude-sonnet-4-20250514",
model: str = MODEL_AUXILIARY,
force_regenerate: bool = False,
) -> ApplicationContext:
"""Generate application context using LLM analysis.
Expand Down Expand Up @@ -503,19 +511,14 @@ def generate_application_context(

# Call LLM
print(f"Generating context with {model}...", file=sys.stderr)
client = Anthropic()
response = client.messages.create(
model=model,
# AnthropicClient is the SDK-backed wrapper; routes through the Claude
# Agent SDK so this works with both API keys and local Claude Code sessions.
client = AnthropicClient(model=model)
response_text = client.analyze_sync(
CONTEXT_GENERATION_PROMPT.format(sources=sources_text),
max_tokens=2000,
messages=[{
"role": "user",
"content": CONTEXT_GENERATION_PROMPT.format(sources=sources_text)
}]
)

# Parse response
response_text = response.content[0].text

# Extract JSON from response
json_match = re.search(r'```json\s*(.*?)\s*```', response_text, re.DOTALL)
if json_match:
Expand Down
5 changes: 3 additions & 2 deletions libs/openant-core/context/generate_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# Add parent directory to path for imports
sys.path.insert(0, str(Path(__file__).parent.parent))

from utilities.model_config import MODEL_AUXILIARY
from context.application_context import (
ApplicationType,
APPLICATION_TYPE_INFO,
Expand Down Expand Up @@ -78,8 +79,8 @@ def main():

parser.add_argument(
"--model", "-m",
default="claude-sonnet-4-20250514",
help="Anthropic model to use (default: claude-sonnet-4-20250514)",
default=MODEL_AUXILIARY,
help=f"Anthropic model to use (default: {MODEL_AUXILIARY})",
)

parser.add_argument(
Expand Down
3 changes: 2 additions & 1 deletion libs/openant-core/core/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,8 @@ def run_analysis(
checkpoint.dir = checkpoint_path

# Select model
model_id = "claude-opus-4-6" if model == "opus" else "claude-sonnet-4-20250514"
from utilities.model_config import MODEL_AUXILIARY, MODEL_PRIMARY
model_id = MODEL_PRIMARY if model == "opus" else MODEL_AUXILIARY
print(f"[Analyze] Model: {model_id}", file=sys.stderr)

# Initialize client
Expand Down
3 changes: 2 additions & 1 deletion libs/openant-core/core/enhancer.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ def enhance_dataset(
# Configure global rate limiter
configure_rate_limiter(backoff_seconds=float(backoff_seconds))

model_id = "claude-sonnet-4-20250514" if model == "sonnet" else "claude-opus-4-6"
from utilities.model_config import MODEL_AUXILIARY, MODEL_PRIMARY
model_id = MODEL_AUXILIARY if model == "sonnet" else MODEL_PRIMARY
print(f"[Enhance] Mode: {mode}", file=sys.stderr)
print(f"[Enhance] Model: {model_id}", file=sys.stderr)

Expand Down
3 changes: 2 additions & 1 deletion libs/openant-core/core/reporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -587,11 +587,12 @@ def _record_usage_in_tracker(usage: dict):
"""Record usage in the global TokenTracker so step_context captures it."""
try:
from utilities.llm_client import get_global_tracker
from utilities.model_config import MODEL_PRIMARY
tracker = get_global_tracker()
# Record as a single aggregated call
if usage.get("total_tokens", 0) > 0:
tracker.record_call(
model="claude-opus-4-6",
model=MODEL_PRIMARY,
input_tokens=usage["input_tokens"],
output_tokens=usage["output_tokens"],
)
Expand Down
3 changes: 2 additions & 1 deletion libs/openant-core/experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,8 @@ def run_experiment(
Experiment results with metrics
"""
# Select model
model_id = "claude-opus-4-20250514" if model == "opus" else "claude-sonnet-4-20250514"
from utilities.model_config import MODEL_AUXILIARY, MODEL_PRIMARY
model_id = MODEL_PRIMARY if model == "opus" else MODEL_AUXILIARY
print(f"Using model: {model_id}")
print(f"Enhanced context: {enhanced}")
print(f"Context correction: {correct_context}")
Expand Down
30 changes: 16 additions & 14 deletions libs/openant-core/generate_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,26 @@
import json
import html
import os
import sys
from datetime import datetime
from pathlib import Path

import anthropic
from dotenv import load_dotenv

# Ensure libs/openant-core is on sys.path so `utilities.*` imports resolve
# regardless of the caller's working directory.
_OPENANT_CORE_ROOT = str(Path(__file__).parent)
if _OPENANT_CORE_ROOT not in sys.path:
sys.path.insert(0, _OPENANT_CORE_ROOT)

from utilities.model_config import MODEL_AUXILIARY # noqa: E402
from utilities.llm_client import AnthropicClient # noqa: E402

# Load environment variables from .env file
load_dotenv()


REPORT_MODEL = "claude-sonnet-4-20250514"
REPORT_MODEL = MODEL_AUXILIARY
MAX_TOKENS = 4096


Expand Down Expand Up @@ -198,18 +208,10 @@ def generate_remediation_guidance(findings: list) -> str:
{findings_text}
"""

api_key = os.getenv("ANTHROPIC_API_KEY")
if not api_key:
raise ValueError("ANTHROPIC_API_KEY not found in environment")

client = anthropic.Anthropic(api_key=api_key)
response = client.messages.create(
model=REPORT_MODEL,
max_tokens=MAX_TOKENS,
messages=[{"role": "user", "content": prompt}]
)

return response.content[0].text
# AnthropicClient is the SDK-backed wrapper; it handles auth (API key
# or local Claude Code session) and surfaces structured SDK errors.
client = AnthropicClient(model=REPORT_MODEL)
return client.analyze_sync(prompt, max_tokens=MAX_TOKENS)


def _build_pipeline_costs_html(step_reports: list[dict]) -> str:
Expand Down
26 changes: 9 additions & 17 deletions libs/openant-core/openant/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -587,10 +587,9 @@ def cmd_report_data(args):
and step reports — everything display-ready.
"""
import html as html_mod
import anthropic
from core.schemas import success, error
from core.step_report import step_context
from utilities.llm_client import get_global_tracker
from utilities.llm_client import AnthropicClient, get_global_tracker

results_path = args.results
dataset_path = args.dataset
Expand Down Expand Up @@ -810,13 +809,10 @@ def cmd_report_data(args):
{findings_text}
"""
print("[Report] Generating remediation guidance (LLM)...", file=sys.stderr)
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=4096,
messages=[{"role": "user", "content": prompt}],
)
remediation_html = response.content[0].text
from utilities.model_config import MODEL_AUXILIARY
# AnthropicClient handles usage tracking via the global TokenTracker.
remediation_client = AnthropicClient(model=MODEL_AUXILIARY)
remediation_html = remediation_client.analyze_sync(prompt, max_tokens=4096)

# Post-process: linkify finding references like #4, #12-#14
import re
Expand All @@ -825,15 +821,11 @@ def _linkify_finding(m):
return f'<a href="#finding-{num}" class="finding-ref">#{num}</a>'
remediation_html = re.sub(r'#(\d+)', _linkify_finding, remediation_html)

# Track usage
usage = response.usage
tracker = get_global_tracker()
tracker.record_call(
model="claude-sonnet-4-20250514",
input_tokens=usage.input_tokens,
output_tokens=usage.output_tokens,
last = remediation_client.get_last_call() or {}
print(
f" Remediation cost: ${last.get('cost_usd', 0.0):.4f}",
file=sys.stderr,
)
print(f" Remediation cost: ${(usage.input_tokens / 1e6) * 3.0 + (usage.output_tokens / 1e6) * 15.0:.4f}", file=sys.stderr)

# --- Step reports ---
step_reports_data = []
Expand Down
Loading
Loading