mirror of
https://github.com/allexanderbergmns/xh1-research.git
synced 2026-08-26 17:07:01 +00:00
setup
This commit is contained in:
Executable
+287
@@ -0,0 +1,287 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# ============================================================
|
||||
# XH-1 Research Harness
|
||||
# Autonomous Research / Review / Revision Pipeline
|
||||
#
|
||||
# Pure Bash
|
||||
# OpenAI-compatible APIs
|
||||
# Designed for OpenRouter
|
||||
# ============================================================
|
||||
|
||||
ROOT="${XH1_RESEARCH_ROOT:-research}"
|
||||
CONFIG="${XH1_RESEARCH_CONFIG:-xh1-research.conf}"
|
||||
STATE="${ROOT}/.xh1"
|
||||
|
||||
mkdir -p \
|
||||
"$STATE/logs" \
|
||||
"$STATE/responses" \
|
||||
"$STATE/runs"
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Defaults
|
||||
# ------------------------------------------------------------
|
||||
|
||||
API_BASE_URL="${API_BASE_URL:-https://openrouter.ai/api/v1}"
|
||||
API_KEY_ENV="${API_KEY_ENV:-OPENROUTER_API_KEY}"
|
||||
|
||||
RESEARCH_MODEL="${RESEARCH_MODEL:-}"
|
||||
REVIEW_MODEL="${REVIEW_MODEL:-$RESEARCH_MODEL}"
|
||||
REVISION_MODEL="${REVISION_MODEL:-$RESEARCH_MODEL}"
|
||||
|
||||
MAX_RESEARCH_ROUNDS="${MAX_RESEARCH_ROUNDS:-3}"
|
||||
MAX_API_RETRIES="${MAX_API_RETRIES:-3}"
|
||||
|
||||
DELAY_SECONDS="${DELAY_SECONDS:-5}"
|
||||
MAX_ITERATIONS="${MAX_ITERATIONS:-0}"
|
||||
|
||||
REVIEW_ENABLED="${REVIEW_ENABLED:-true}"
|
||||
AUTO_COMMIT="${AUTO_COMMIT:-false}"
|
||||
|
||||
RUN_ID=""
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# General
|
||||
# ------------------------------------------------------------
|
||||
|
||||
die() {
|
||||
echo "ERROR: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
log() {
|
||||
local message="$*"
|
||||
|
||||
echo "[$(date -u '+%Y-%m-%dT%H:%M:%SZ')] $message" \
|
||||
| tee -a "$STATE/logs/harness.log"
|
||||
}
|
||||
|
||||
require() {
|
||||
command -v "$1" >/dev/null 2>&1 ||
|
||||
die "Required command not found: $1"
|
||||
}
|
||||
|
||||
require_dependencies() {
|
||||
require bash
|
||||
require curl
|
||||
require jq
|
||||
require find
|
||||
require grep
|
||||
require sed
|
||||
require awk
|
||||
}
|
||||
|
||||
timestamp() {
|
||||
date -u '+%Y%m%dT%H%M%SZ'
|
||||
}
|
||||
|
||||
load_config() {
|
||||
if [[ -f "$CONFIG" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
source "$CONFIG"
|
||||
fi
|
||||
}
|
||||
|
||||
get_api_key() {
|
||||
local key="${!API_KEY_ENV:-}"
|
||||
|
||||
[[ -n "$key" ]] ||
|
||||
die "Missing API key. Set ${API_KEY_ENV}."
|
||||
|
||||
printf '%s' "$key"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Configuration
|
||||
# ------------------------------------------------------------
|
||||
|
||||
init_config() {
|
||||
|
||||
if [[ -f "$CONFIG" ]]; then
|
||||
echo "Configuration already exists:"
|
||||
echo " $CONFIG"
|
||||
return
|
||||
fi
|
||||
|
||||
cat > "$CONFIG" <<'CONFIG'
|
||||
# ============================================================
|
||||
# XH-1 Research Harness Configuration
|
||||
# ============================================================
|
||||
|
||||
# OpenAI-compatible API endpoint.
|
||||
# OpenRouter:
|
||||
API_BASE_URL="https://openrouter.ai/api/v1"
|
||||
|
||||
# Environment variable containing the API key.
|
||||
API_KEY_ENV="OPENROUTER_API_KEY"
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Models
|
||||
# ------------------------------------------------------------
|
||||
|
||||
# Model used for initial research.
|
||||
RESEARCH_MODEL=""
|
||||
|
||||
# Model used for independent review.
|
||||
REVIEW_MODEL=""
|
||||
|
||||
# Model used to revise rejected research.
|
||||
REVISION_MODEL=""
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Research behavior
|
||||
# ------------------------------------------------------------
|
||||
|
||||
# Maximum Research -> Review -> Revision cycles for ONE topic.
|
||||
MAX_RESEARCH_ROUNDS=3
|
||||
|
||||
# API retries per individual request.
|
||||
MAX_API_RETRIES=3
|
||||
|
||||
# Delay between topics.
|
||||
DELAY_SECONDS=5
|
||||
|
||||
# 0 = unlimited.
|
||||
MAX_ITERATIONS=0
|
||||
|
||||
# Enable independent reviewer.
|
||||
REVIEW_ENABLED=true
|
||||
|
||||
# Automatically create git commits.
|
||||
AUTO_COMMIT=false
|
||||
CONFIG
|
||||
|
||||
echo "Created $CONFIG"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Run state
|
||||
# ------------------------------------------------------------
|
||||
|
||||
start_run() {
|
||||
|
||||
RUN_ID="$(timestamp)"
|
||||
|
||||
mkdir -p "$STATE/runs/$RUN_ID"
|
||||
|
||||
: > "$STATE/runs/$RUN_ID/completed"
|
||||
: > "$STATE/runs/$RUN_ID/failed"
|
||||
: > "$STATE/runs/$RUN_ID/attempts"
|
||||
|
||||
log "Started run: $RUN_ID"
|
||||
}
|
||||
|
||||
run_has_completed() {
|
||||
local file="$1"
|
||||
|
||||
grep -Fxq "$file" \
|
||||
"$STATE/runs/$RUN_ID/completed" 2>/dev/null
|
||||
}
|
||||
|
||||
run_has_failed() {
|
||||
local file="$1"
|
||||
|
||||
grep -Fxq "$file" \
|
||||
"$STATE/runs/$RUN_ID/failed" 2>/dev/null
|
||||
}
|
||||
|
||||
mark_completed() {
|
||||
local file="$1"
|
||||
|
||||
printf '%s\n' "$file" \
|
||||
>> "$STATE/runs/$RUN_ID/completed"
|
||||
}
|
||||
|
||||
mark_failed() {
|
||||
local file="$1"
|
||||
|
||||
printf '%s\n' "$file" \
|
||||
>> "$STATE/runs/$RUN_ID/failed"
|
||||
}
|
||||
|
||||
record_attempt() {
|
||||
|
||||
local file="$1"
|
||||
local round="$2"
|
||||
local phase="$3"
|
||||
local result="$4"
|
||||
|
||||
printf '%s\t%s\t%s\t%s\t%s\n' \
|
||||
"$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
|
||||
"$file" \
|
||||
"$round" \
|
||||
"$phase" \
|
||||
"$result" \
|
||||
>> "$STATE/runs/$RUN_ID/attempts"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Research discovery
|
||||
# ------------------------------------------------------------
|
||||
|
||||
is_soon() {
|
||||
grep -qE '^SOON[[:space:]]*$' "$1"
|
||||
}
|
||||
|
||||
find_next_task() {
|
||||
|
||||
while IFS= read -r -d '' file; do
|
||||
|
||||
[[ "$file" == "$STATE"/* ]] && continue
|
||||
|
||||
if is_soon "$file"; then
|
||||
|
||||
if ! run_has_completed "$file" &&
|
||||
! run_has_failed "$file"
|
||||
then
|
||||
printf '%s\n' "$file"
|
||||
return 0
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
done < <(
|
||||
find "$ROOT" \
|
||||
-type f \
|
||||
-name '*.md' \
|
||||
-not -path "$STATE/*" \
|
||||
-print0
|
||||
)
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Context
|
||||
# ------------------------------------------------------------
|
||||
|
||||
build_context() {
|
||||
|
||||
local file="$1"
|
||||
|
||||
local relative="${file#"$ROOT"/}"
|
||||
local area="${relative%%/*}"
|
||||
|
||||
cat <<EOF
|
||||
XH-1 RESEARCH PROJECT
|
||||
=====================
|
||||
|
||||
Project:
|
||||
XH-1
|
||||
|
||||
Architecture:
|
||||
Custom 128-core RISC-V processor
|
||||
|
||||
Research repository:
|
||||
XH-1 Research
|
||||
|
||||
Current document:
|
||||
$file
|
||||
|
||||
Research area:
|
||||
$area
|
||||
|
||||
CURRENT DOCUMENT
|
||||
================
|
||||
|
||||
Reference in New Issue
Block a user