Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
31 changes: 31 additions & 0 deletions .github/workflows/hai-agent-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: HAI Agent Branch Check

on:
push:
branches:
- "**"
pull_request:
branches:
- "**"
workflow_dispatch:

jobs:
run-hai-agent:
if: github.event_name != 'push' || startsWith(github.ref, 'refs/heads/')
runs-on: ubuntu-latest
permissions:
contents: read

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Run HAI agent with cache fallback
env:
HAI_SOURCE_URL: ${{ vars.HAI_SOURCE_URL }}
run: python scripts/run_hai_agent.py
29 changes: 29 additions & 0 deletions .github/workflows/production-workflow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Production Workflow

on:
push:
branches:
- main
workflow_dispatch:

permissions:
contents: read

concurrency:
group: production-workflow-${{ github.ref }}
cancel-in-progress: true

jobs:
production-check:
name: Run production workflow
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Verify key production assets
run: |
test -f index.html || { echo "Production check failed: missing /index.html at repository root."; exit 1; }
test -f robots.txt || { echo "Production check failed: missing /robots.txt at repository root."; exit 1; }
test -f sitemap.xml || { echo "Production check failed: missing /sitemap.xml at repository root."; exit 1; }
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
__pycache__/
*.pyc
Binary file added data/hai_cache.db
Binary file not shown.
119 changes: 119 additions & 0 deletions scripts/run_hai_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#!/usr/bin/env python3
"""Run HAI agent with network-first mode and SQLite cache fallback."""

from __future__ import annotations

import json
import os
import sqlite3
import sys
import urllib.error
import urllib.request
from datetime import datetime, timezone
from pathlib import Path
from typing import Any


REPO_ROOT = Path(__file__).resolve().parents[1]
DEFAULT_DB_PATH = REPO_ROOT / "data" / "hai_cache.db"


def ensure_cache_schema(connection: sqlite3.Connection) -> None:
connection.execute(
"""
CREATE TABLE IF NOT EXISTS hai_snapshots (
id INTEGER PRIMARY KEY AUTOINCREMENT,
recorded_at TEXT NOT NULL,
source TEXT NOT NULL,
payload_json TEXT NOT NULL
)
"""
)
connection.commit()


def fetch_live_payload(url: str, timeout: int) -> dict[str, Any]:
request = urllib.request.Request(
url,
headers={"Accept": "application/json", "User-Agent": "pollnex-hai-agent/1.0"},
)
with urllib.request.urlopen(request, timeout=timeout) as response:
body = response.read().decode("utf-8")
parsed = json.loads(body)
if isinstance(parsed, dict):
return parsed
return {"value": parsed}


def save_snapshot(connection: sqlite3.Connection, source: str, payload: dict[str, Any]) -> None:
connection.execute(
"""
INSERT INTO hai_snapshots (recorded_at, source, payload_json)
VALUES (?, ?, ?)
""",
(
datetime.now(timezone.utc).isoformat(),
source,
json.dumps(payload, sort_keys=True, separators=(",", ":")),
),
)
connection.commit()


def load_latest_snapshot(connection: sqlite3.Connection) -> dict[str, Any] | None:
row = connection.execute(
"""
SELECT payload_json
FROM hai_snapshots
ORDER BY id DESC
LIMIT 1
"""
).fetchone()
if not row:
return None
return json.loads(row[0])


def normalize_payload(payload: dict[str, Any]) -> dict[str, Any]:
normalized = dict(payload)
if "hai_score" not in normalized and "score" in normalized:
normalized["hai_score"] = normalized["score"]
return normalized


def main() -> int:
db_path = Path(os.getenv("HAI_CACHE_DB", str(DEFAULT_DB_PATH)))
source_url = os.getenv("HAI_SOURCE_URL", "").strip()
timeout_seconds = int(os.getenv("HAI_REQUEST_TIMEOUT_SECONDS", "8"))

db_path.parent.mkdir(parents=True, exist_ok=True)
with sqlite3.connect(db_path) as connection:
ensure_cache_schema(connection)

runtime_mode = "cached"
payload: dict[str, Any] | None = None

if source_url:
try:
payload = normalize_payload(fetch_live_payload(source_url, timeout_seconds))
save_snapshot(connection, "network", payload)
runtime_mode = "network"
except (urllib.error.URLError, TimeoutError, OSError, json.JSONDecodeError) as error:
print(f"[WARN] Network source unavailable, falling back to cached DB: {error}")

if payload is None:
payload = load_latest_snapshot(connection)
if payload is None:
print(
"[ERROR] No cached HAI snapshot found and network source is unavailable.",
file=sys.stderr,
)
return 1

print(f"[INFO] HAI agent runtime mode: {runtime_mode}")
print(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True))
return 0


if __name__ == "__main__":
raise SystemExit(main())
Loading