Files
wehub-resource-sync 426e9eeabd
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Waiting to run
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Waiting to run
Build Agent Image / build-and-push (push) Waiting to run
Chat shell gestures / Chat shell gesture + parity e2e (push) Waiting to run
ci / test (push) Waiting to run
ci / lint-and-format (push) Waiting to run
ci / build (push) Waiting to run
ci / dev-startup (push) Waiting to run
Cloud Gateway Discord / Test (push) Waiting to run
Cloud Gateway Webhook / Test (push) Waiting to run
Cloud Tests / lint-and-types (push) Waiting to run
Cloud Tests / unit-tests (push) Waiting to run
Cloud Tests / integration-tests (push) Waiting to run
Cloud Tests / e2e-tests (push) Blocked by required conditions
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Deploy Apps Worker (Product 2) / Determine environment (push) Waiting to run
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Blocked by required conditions
Deploy Eliza Provisioning Worker / Determine environment (push) Waiting to run
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Blocked by required conditions
Dev Smoke / Classify changed paths (push) Waiting to run
Dev Smoke / bun run dev onboarding chat (push) Blocked by required conditions
Dev Smoke / Vite HMR dependency-level smoke (push) Blocked by required conditions
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Waiting to run
gitleaks / gitleaks (push) Waiting to run
Markdown Links / Relative Markdown Links (push) Waiting to run
Publish @elizaos/example-code / check_npm (push) Waiting to run
Publish @elizaos/example-code / publish_npm (push) Blocked by required conditions
Publish @elizaos/plugin-elizacloud / verify_version (push) Waiting to run
Publish @elizaos/plugin-elizacloud / publish_npm (push) Blocked by required conditions
Quality (Extended) / Homepage Build (PR smoke) (push) Waiting to run
Quality (Extended) / Comment-only diff guard (push) Waiting to run
Quality (Extended) / Format + Type Safety Ratchet (push) Waiting to run
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Waiting to run
Quality (Extended) / Develop Gate (lint) (push) Waiting to run
Sandbox Live Smoke / Sandbox live smoke (push) Waiting to run
Snap Build & Test / Build Snap (amd64) (push) Waiting to run
Snap Build & Test / Build Snap (arm64) (push) Waiting to run
supply-chain / sbom (push) Waiting to run
supply-chain / vulnerability-scan (push) Waiting to run
Build, Push & Deploy to Phala Cloud / build-and-push (push) Waiting to run
Test Packaging / Validate Packaging Configs (push) Waiting to run
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Waiting to run
Test Packaging / Pack & Test JS Tarballs (push) Waiting to run
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Waiting to run
UI Fixture E2E / ui-fixture-e2e (push) Waiting to run
UI Fixture E2E / fixture-e2e (push) Waiting to run
UI Story Gate / story-gate (push) Waiting to run
vault-ci / test (macos-latest) (push) Waiting to run
vault-ci / test (ubuntu-latest) (push) Waiting to run
vault-ci / test (windows-latest) (push) Waiting to run
vault-ci / app-core wiring tests (push) Waiting to run
verify-patches / verify patches/CHECKSUMS.sha256 (push) Waiting to run
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Waiting to run
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Waiting to run
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Waiting to run
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Waiting to run
Voice Benchmark Smoke / voice bench smoke summary (push) Blocked by required conditions
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Waiting to run
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Waiting to run
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Waiting to run
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Waiting to run
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Waiting to run
Test Packaging / Build & Test PyPI Package (push) Waiting to run
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:43:05 +08:00

424 lines
16 KiB
YAML

name: Feed RL Training
# Trigger options:
# 1. On schedule (daily training at 2 AM UTC)
# 2. Via repository_dispatch (from debug endpoint)
# 3. Manual workflow_dispatch (testing from GitHub UI)
on:
schedule:
# Daily at 2 AM UTC
- cron: '0 2 * * *'
repository_dispatch:
types: [trigger-training]
workflow_dispatch:
inputs:
batch_id:
description: 'Training batch ID (optional, auto-generated if not provided)'
required: false
type: string
window_id:
description: 'Window ID to train (optional, auto-detected if not provided)'
required: false
type: string
force:
description: 'Force training even if not ready (for testing)'
required: false
type: boolean
default: false
base_model:
description: 'Base model to use (default: google/gemma-4-E4B-it)'
required: false
type: string
env:
PYTHON_VERSION: '3.11'
FEED_DEFAULT_BASE_MODEL: 'google/gemma-4-E4B-it'
# Prevent concurrent training runs
concurrency:
group: training-pipeline
cancel-in-progress: false # Wait for current run to finish
permissions:
contents: read
jobs:
train:
if: ${{ vars.ENABLE_RL_TRAINING == 'true' }}
name: Train RL Model
runs-on: ${{ fromJSON(vars.HETZNER_FLEET_ONLINE == 'false' && '["ubuntu-24.04"]' || '["self-hosted","hetzner-robot"]') }}
timeout-minutes: 360 # 6 hours max
steps:
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
- name: Setup Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
cache-dependency-path: 'packages/training/scripts/rl/requirements.txt'
- name: Install dependencies
working-directory: packages/training/scripts/rl
run: |
pip install --upgrade pip
pip install -r requirements.txt
pip install -e .
- name: Verify installation
run: |
python --version
pip list | grep -E "(art|asyncpg)"
- name: Check training readiness
id: readiness
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: |
python -c "
import asyncio
import asyncpg
import os
import sys
async def check():
try:
pool = await asyncpg.create_pool(
os.getenv('DATABASE_URL'),
min_size=1,
max_size=2,
timeout=30
)
# Count scored trajectories ready for training
count = await pool.fetchval('''
SELECT COUNT(*) FROM trajectories
WHERE \"isTrainingData\" = true
AND \"usedInTraining\" = false
AND \"aiJudgeReward\" IS NOT NULL
AND \"stepsJson\" IS NOT NULL
AND \"stepsJson\"::text != 'null'
AND \"stepsJson\"::text != '[]'
''')
print(f'✅ Database connected')
print(f'📊 Trajectories ready for training: {count}')
# Need 100 bundles minimum
min_required = 100
ready = count >= min_required
if ready:
print(f'✅ READY: {count} >= {min_required}')
else:
print(f'⏳ NOT READY: {count} < {min_required} (need {min_required - count} more)')
with open(os.getenv('GITHUB_OUTPUT'), 'a') as f:
f.write(f'ready={str(ready).lower()}\\n')
f.write(f'count={count}\\n')
await pool.close()
except Exception as e:
print(f'❌ Error checking readiness: {e}', file=sys.stderr)
with open(os.getenv('GITHUB_OUTPUT'), 'a') as f:
f.write('ready=false\\n')
f.write('count=0\\n')
sys.exit(1)
asyncio.run(check())
"
- name: Get batch info
id: batch
env:
# From workflow_dispatch inputs:
BATCH_ID_INPUT: ${{ inputs.batch_id || '' }}
WINDOW_ID_INPUT: ${{ inputs.window_id || '' }}
FORCE_INPUT: ${{ inputs.force || 'false' }}
BASE_MODEL_INPUT: ${{ inputs.base_model || '' }}
# From repository_dispatch payload:
BATCH_ID_PAYLOAD: ${{ github.event.client_payload.batch_id || '' }}
WINDOW_ID_PAYLOAD: ${{ github.event.client_payload.window_id || '' }}
FORCE_PAYLOAD: ${{ github.event.client_payload.force || 'false' }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: |
# Determine batch_id
if [ -n "$BATCH_ID_PAYLOAD" ]; then
echo "batch_id=$BATCH_ID_PAYLOAD" >> $GITHUB_OUTPUT
elif [ -n "$BATCH_ID_INPUT" ]; then
echo "batch_id=$BATCH_ID_INPUT" >> $GITHUB_OUTPUT
else
echo "batch_id=batch-$(date +%s)" >> $GITHUB_OUTPUT
fi
# Determine window_id
if [ -n "$WINDOW_ID_PAYLOAD" ]; then
echo "window_id=$WINDOW_ID_PAYLOAD" >> $GITHUB_OUTPUT
elif [ -n "$WINDOW_ID_INPUT" ]; then
echo "window_id=$WINDOW_ID_INPUT" >> $GITHUB_OUTPUT
else
WINDOW=$(date -u +"%Y-%m-%dT%H:00")
echo "window_id=$WINDOW" >> $GITHUB_OUTPUT
fi
# Determine force flag
if [ "$FORCE_PAYLOAD" = "true" ] || [ "$FORCE_INPUT" = "true" ]; then
echo "force=true" >> $GITHUB_OUTPUT
else
echo "force=false" >> $GITHUB_OUTPUT
fi
# Determine base model
if [ -n "$BASE_MODEL_INPUT" ]; then
echo "base_model=$BASE_MODEL_INPUT" >> $GITHUB_OUTPUT
else
echo "base_model=$FEED_DEFAULT_BASE_MODEL" >> $GITHUB_OUTPUT
fi
# Generate model version
MODEL_VERSION=$(python -c "
import asyncio
import asyncpg
import os
import sys
async def get_version():
try:
pool = await asyncpg.create_pool(os.getenv('DATABASE_URL'), timeout=10)
latest = await pool.fetchval('''
SELECT version FROM trained_models
WHERE status IN ('ready', 'deployed')
ORDER BY \"createdAt\" DESC
LIMIT 1
''')
if latest:
parts = latest.strip('v').split('.')
patch = int(parts[2]) + 1
version = f'v{parts[0]}.{parts[1]}.{patch}'
else:
version = 'v1.0.0'
print(version)
await pool.close()
except Exception as e:
import time
version = f'v1.0.{int(time.time()) % 10000}'
print(version)
asyncio.run(get_version())
" 2>/dev/null)
echo "model_version=$MODEL_VERSION" >> $GITHUB_OUTPUT
echo "source=github_cron" >> $GITHUB_OUTPUT
- name: Skip if not ready (unless forced)
if: steps.readiness.outputs.ready != 'true' && steps.batch.outputs.force != 'true'
run: |
echo "⏭️ Not ready for training and force=false"
echo "Trajectories: ${{ steps.readiness.outputs.count }}"
echo "Required: 100 (minimum bundles)"
exit 0
- name: Update batch status to training
if: steps.readiness.outputs.ready == 'true' || steps.batch.outputs.force == 'true'
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
BATCH_ID: ${{ steps.batch.outputs.batch_id }}
run: |
python -c "
import asyncio
import asyncpg
import os
async def update():
pool = await asyncpg.create_pool(os.getenv('DATABASE_URL'))
batch_id = os.getenv('BATCH_ID')
async with pool.acquire() as conn:
async with conn.transaction():
await conn.execute('''
INSERT INTO training_batches (
\"batchId\", id, status, \"startedAt\", \"createdAt\"
) VALUES (
\$1, \$1, 'training', NOW(), NOW()
)
ON CONFLICT (\"batchId\")
DO UPDATE SET status = 'training', \"startedAt\" = NOW()
''', batch_id)
print(f'✅ Batch {batch_id} status: training')
await pool.close()
asyncio.run(update())
"
- name: Select base model
id: model_selection
if: steps.readiness.outputs.ready == 'true' || steps.batch.outputs.force == 'true'
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
BASE_MODEL_OVERRIDE: ${{ steps.batch.outputs.base_model }}
run: |
python -c "
import asyncio
import asyncpg
import os
import sys
async def select_model():
try:
pool = await asyncpg.create_pool(os.getenv('DATABASE_URL'))
base_model_override = os.getenv('BASE_MODEL_OVERRIDE', '')
default_base_model = os.getenv('FEED_DEFAULT_BASE_MODEL', 'google/gemma-4-E4B-it')
# Count training bundles
bundle_count = await pool.fetchval('''
SELECT COUNT(*) FROM trajectories
WHERE \"isTrainingData\" = true
AND \"usedInTraining\" = false
AND \"aiJudgeReward\" IS NOT NULL
AND \"stepsJson\" IS NOT NULL
AND \"stepsJson\"::text != 'null'
AND \"stepsJson\"::text != '[]'
''')
print(f'📊 Bundle count: {bundle_count}')
# Use override if provided, otherwise use default
if base_model_override:
base_model = base_model_override
strategy = 'override'
else:
base_model = default_base_model
strategy = 'default'
print(f'📦 Selected model: {base_model}')
print(f'📋 Strategy: {strategy}')
with open(os.getenv('GITHUB_OUTPUT'), 'a') as f:
f.write(f'base_model={base_model}\\n')
f.write(f'strategy={strategy}\\n')
f.write(f'bundle_count={bundle_count}\\n')
await pool.close()
except Exception as e:
print(f'❌ Model selection failed: {e}', file=sys.stderr)
default_base_model = os.getenv('FEED_DEFAULT_BASE_MODEL', 'google/gemma-4-E4B-it')
with open(os.getenv('GITHUB_OUTPUT'), 'a') as f:
f.write(f'base_model={default_base_model}\\n')
f.write('strategy=fallback\\n')
sys.exit(1)
asyncio.run(select_model())
"
- name: Run RL Training
id: training
if: steps.readiness.outputs.ready == 'true' || steps.batch.outputs.force == 'true'
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
BATCH_ID: ${{ steps.batch.outputs.batch_id }}
WINDOW_ID: ${{ steps.batch.outputs.window_id }}
MODEL_VERSION: ${{ steps.batch.outputs.model_version }}
BASE_MODEL: ${{ steps.model_selection.outputs.base_model }}
MODE: single
MAX_EXAMPLES: "2000"
MAX_STEPS_PER_TRAJECTORY: "20"
MAX_SEQ_LENGTH: "8192"
working-directory: packages/training/scripts/rl
run: |
echo "🚀 Starting training"
echo "Batch ID: $BATCH_ID"
echo "Window ID: $WINDOW_ID"
echo "Model Version: $MODEL_VERSION"
echo "Strategy: ${{ steps.model_selection.outputs.strategy }}"
echo "Base Model: $BASE_MODEL"
echo "Trajectories available: ${{ steps.readiness.outputs.count }}"
echo "Bundle count: ${{ steps.model_selection.outputs.bundle_count }}"
# Run trainer
python src/training/feed_trainer.py
- name: Update batch status to completed
if: success() && (steps.readiness.outputs.ready == 'true' || steps.batch.outputs.force == 'true')
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
BATCH_ID: ${{ steps.batch.outputs.batch_id }}
run: |
python -c "
import asyncio
import asyncpg
import os
async def update():
pool = await asyncpg.create_pool(os.getenv('DATABASE_URL'))
batch_id = os.getenv('BATCH_ID')
await pool.execute('''
UPDATE training_batches
SET status = 'completed', \"completedAt\" = NOW()
WHERE \"batchId\" = \$1
''', batch_id)
print(f'✅ Batch {batch_id} completed')
await pool.close()
asyncio.run(update())
"
- name: Update batch status to failed
if: failure() && (steps.readiness.outputs.ready == 'true' || steps.batch.outputs.force == 'true')
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
BATCH_ID: ${{ steps.batch.outputs.batch_id }}
run: |
python -c "
import asyncio
import asyncpg
import os
async def update():
pool = await asyncpg.create_pool(os.getenv('DATABASE_URL'))
batch_id = os.getenv('BATCH_ID')
await pool.execute('''
UPDATE training_batches
SET status = 'failed', error = 'GitHub Actions workflow failed'
WHERE \"batchId\" = \$1
''', batch_id)
print(f'❌ Batch {batch_id} failed')
await pool.close()
asyncio.run(update())
"
- name: Upload training logs
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: training-logs-${{ steps.batch.outputs.batch_id }}
path: |
packages/training/scripts/rl/logs/
packages/training/scripts/rl/*.log
retention-days: 7
- name: Report status
if: always()
run: |
echo "Training Status: ${{ job.status }}"
echo "Batch ID: ${{ steps.batch.outputs.batch_id }}"
echo "Window ID: ${{ steps.batch.outputs.window_id }}"
echo "Ready: ${{ steps.readiness.outputs.ready }}"
echo "Trajectories: ${{ steps.readiness.outputs.count }}"
echo "Model: ${{ steps.model_selection.outputs.base_model }}"