Skip to content
This repository was archived by the owner on Jun 19, 2026. It is now read-only.

Commit a1ef90c

Browse files
chore: Multiple Tests and docs (#4)
* chore: test matrix * chore: fix github typo * chore: fix ci convert to python * chore: parallel more * chore: fix yml * chore: python errors * chore: typo * chore: fix matrix * chore: fix array matri * chore: fix matrix * chore: avoid 429 * chore: fix name * chore: fix artifact valid name * chore: fix artifact consolidation * chore: fix artifact consolidation * chore: fix consolidation * chore: add security
1 parent f23a35e commit a1ef90c

27 files changed

Lines changed: 930 additions & 626 deletions

File tree

.github/workflows/skill-validation.yml

Lines changed: 62 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -9,185 +9,120 @@ permissions:
99
contents: read
1010

1111
jobs:
12-
# Job 1: Generate matrix from config
1312
prepare:
1413
runs-on: ubuntu-latest
1514
if: github.event.issue.pull_request && contains(github.event.comment.body, '/test') && github.actor == 'Ariel-Rodriguez'
1615
outputs:
17-
matrix: ${{ steps.generate-matrix.outputs.matrix }}
18-
filter: ${{ steps.parse.outputs.filter }}
19-
use-parallel: ${{ steps.parse.outputs.use-parallel }}
20-
16+
matrix: ${{ steps.matrix.outputs.result }}
17+
2118
steps:
19+
- uses: actions/checkout@v4
20+
- uses: astral-sh/setup-uv@v5
21+
2222
- name: Parse test command
23-
id: parse
23+
id: command
2424
run: |
25-
COMMENT="${{ github.event.comment.body }}"
26-
FILTER="all"
27-
USE_PARALLEL="false"
28-
29-
# Check for specific provider filter
30-
if echo "$COMMENT" | grep -q "/test copilot"; then
31-
FILTER="copilot"
32-
elif echo "$COMMENT" | grep -q "/test ollama"; then
33-
FILTER="ollama"
34-
elif echo "$COMMENT" | grep -q "/test gemini"; then
35-
FILTER="gemini"
36-
elif echo "$COMMENT" | grep -q "/test all"; then
37-
FILTER="all"
38-
fi
39-
40-
# Check for parallel flag
41-
if echo "$COMMENT" | grep -q "parallel"; then
42-
USE_PARALLEL="true"
25+
SKILLS=$(python3 ci/parse_test_command.py "${{ github.event.comment.body }}")
26+
echo "skills=$SKILLS" >> $GITHUB_OUTPUT
27+
if [ -n "$SKILLS" ]; then
28+
echo "Override skills: $SKILLS"
29+
else
30+
echo "Auto-detecting changed skills..."
4331
fi
44-
45-
echo "filter=$FILTER" >> $GITHUB_OUTPUT
46-
echo "use-parallel=$USE_PARALLEL" >> $GITHUB_OUTPUT
47-
echo "Testing with filter: $FILTER (parallel: $USE_PARALLEL)"
48-
49-
- name: Checkout code
50-
uses: actions/checkout@v4
51-
52-
- name: Set up Python
53-
uses: actions/setup-python@v5
54-
with:
55-
python-version: '3.11'
56-
57-
- name: Install PyYAML
58-
run: pip install pyyaml
5932
6033
- name: Generate matrix
61-
id: generate-matrix
62-
run: |
63-
MATRIX=$(python3 ci/matrix_generator.py --filter-provider "${{ steps.parse.outputs.filter }}")
64-
echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
65-
echo "Generated matrix:"
66-
echo "$MATRIX" | jq .
67-
68-
# Job 2: Run evaluations (parallel or sequential based on parse)
69-
evaluate:
70-
needs: prepare
71-
runs-on: ubuntu-latest
72-
if: needs.prepare.outputs.use-parallel == 'false'
73-
74-
steps:
75-
- name: Checkout code
76-
uses: actions/checkout@v4
77-
with:
78-
ref: refs/pull/${{ github.event.issue.number }}/head
79-
80-
- name: Setup Node.js
81-
uses: actions/setup-node@v4
82-
with:
83-
node-version: '20'
84-
85-
- name: Install Copilot CLI
86-
run: npm install -g @github/copilot
87-
88-
- name: Install uv
89-
uses: astral-sh/setup-uv@v5
90-
with:
91-
enable-cache: true
92-
93-
- name: Set up Python
94-
run: uv python install 3.11
95-
96-
- name: Run orchestration
34+
id: matrix
9735
env:
9836
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
99-
COPILOT_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
100-
OLLAMA_API_KEY: ${{ secrets.OLLAMA_API_KEY }}
37+
PR_NUMBER: ${{ github.event.issue.number }}
10138
run: |
102-
chmod +x ci/*.sh
103-
./ci/orchestrate_evaluations.sh ${{ github.event.issue.number }} --filter-provider "${{ needs.prepare.outputs.filter }}"
39+
python3 ci/generate_matrix.py "$PR_NUMBER" ${{ steps.command.outputs.skills }} > /tmp/matrix.json
10440
105-
- name: Post results
106-
uses: marocchino/sticky-pull-request-comment@v2
107-
if: always() && hashFiles('comment.md') != ''
108-
with:
109-
number: ${{ github.event.issue.number }}
110-
path: comment.md
41+
echo "result<<EOF" >> $GITHUB_OUTPUT
42+
cat /tmp/matrix.json >> $GITHUB_OUTPUT
43+
echo "EOF" >> $GITHUB_OUTPUT
44+
45+
python3 -c "import json; m=json.load(open('/tmp/matrix.json')); print(f'Generated {len(m[\"include\"])} jobs')"
11146
112-
# Job 3: Run evaluations in parallel (matrix strategy)
113-
evaluate-parallel:
47+
evaluate:
11448
needs: prepare
11549
runs-on: ubuntu-latest
116-
if: needs.prepare.outputs.use-parallel == 'true'
11750
strategy:
11851
fail-fast: false
52+
max-parallel: 2
11953
matrix: ${{ fromJson(needs.prepare.outputs.matrix) }}
120-
54+
12155
steps:
122-
- name: Checkout code
123-
uses: actions/checkout@v4
124-
with:
125-
ref: refs/pull/${{ github.event.issue.number }}/head
56+
- uses: actions/checkout@v4
12657

127-
- name: Setup Node.js
128-
uses: actions/setup-node@v4
58+
- uses: actions/setup-node@v4
59+
if: matrix.provider == 'copilot'
12960
with:
130-
node-version: '20'
61+
node-version: "20"
13162

13263
- name: Install Copilot CLI
13364
if: matrix.provider == 'copilot'
13465
run: npm install -g @github/copilot
13566

136-
- name: Install uv
137-
uses: astral-sh/setup-uv@v5
67+
- uses: astral-sh/setup-uv@v5
13868
with:
13969
enable-cache: true
14070

141-
- name: Set up Python
142-
run: uv python install 3.11
143-
144-
- name: Detect changes
145-
id: changes
146-
env:
147-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
71+
- name: Set artifact name
72+
id: artifact
14873
run: |
149-
chmod +x ci/detect_changes.sh
150-
MODIFIED_SKILLS=$(./ci/detect_changes.sh ${{ github.event.issue.number }})
151-
echo "modified_skills=$MODIFIED_SKILLS" >> $GITHUB_OUTPUT
74+
ARTIFACT_NAME="results-${{ matrix.provider }}-${{ matrix.model }}-${{ matrix.skill || 'all' }}"
75+
ARTIFACT_NAME="${ARTIFACT_NAME//:/--}"
76+
echo "name=$ARTIFACT_NAME" >> $GITHUB_OUTPUT
15277
153-
- name: Run evaluation for ${{ matrix.display_name }}
78+
- name: Evaluate ${{ matrix.display_name }}
15479
env:
15580
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
15681
COPILOT_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
15782
OLLAMA_API_KEY: ${{ secrets.OLLAMA_API_KEY }}
158-
MODIFIED_SKILLS: ${{ steps.changes.outputs.modified_skills }}
15983
run: |
160-
chmod +x ci/run_evaluation.sh
161-
./ci/run_evaluation.sh "${{ matrix.provider }}" "${{ matrix.model }}" 50 "${{ matrix.extra_args }}"
84+
CMD="uv run --project tests --frozen tests/evaluator.py"
85+
CMD="$CMD --provider ${{ matrix.provider }}"
86+
CMD="$CMD --model ${{ matrix.model }}"
87+
CMD="$CMD --judge --verbose --report --threshold 50"
88+
89+
if [ -n "${{ matrix.extra_args }}" ]; then
90+
CMD="$CMD ${{ matrix.extra_args }}"
91+
fi
92+
93+
if [ -n "${{ matrix.skill }}" ]; then
94+
CMD="$CMD --skill ${{ matrix.skill }}"
95+
else
96+
CMD="$CMD --all"
97+
fi
98+
99+
echo "Running: $CMD"
100+
eval "$CMD"
162101
163102
- name: Upload results
164-
uses: actions/upload-artifact@v4
165103
if: always()
104+
uses: actions/upload-artifact@v4
166105
with:
167-
name: results-${{ matrix.provider }}-${{ matrix.model }}
168-
path: tests/results/${{ matrix.provider }}/${{ matrix.model }}/
106+
name: ${{ steps.artifact.outputs.name }}
107+
path: tests/results/
108+
retention-days: 1
169109

170-
# Job 4: Consolidate parallel results
171110
consolidate:
172-
needs: [prepare, evaluate-parallel]
111+
needs: evaluate
173112
runs-on: ubuntu-latest
174-
if: needs.prepare.outputs.use-parallel == 'true' && always()
175-
113+
if: always()
114+
176115
steps:
177-
- name: Checkout code
178-
uses: actions/checkout@v4
116+
- uses: actions/checkout@v4
179117

180-
- name: Download all artifacts
181-
uses: actions/download-artifact@v4
118+
- uses: actions/download-artifact@v4
182119
with:
183120
path: tests/results/
184121

185122
- name: Consolidate results
186-
run: |
187-
chmod +x ci/consolidate_results.sh
188-
./ci/consolidate_results.sh || true
123+
run: python3 ci/consolidate_results.py
189124

190-
- name: Post consolidated results
125+
- name: Post to PR
191126
uses: marocchino/sticky-pull-request-comment@v2
192127
if: hashFiles('comment.md') != ''
193128
with:

ci/collect-artifacts.sh

Lines changed: 0 additions & 8 deletions
This file was deleted.

ci/config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
ollama:
55
enabled: true
66
local: false
7-
context: 32000
7+
context: 28000
88
models:
9-
- gpt-oss:20b-cloud
109
- rnj-1:8b-cloud
1110
- devstral-small-2:24b-cloud
11+
# - gpt-oss:20b-cloud
1212

1313
copilot:
1414
enabled: false

ci/consolidate_results.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Consolidate results from parallel evaluations.
4+
5+
Aggregates outputs from multiple provider/model runs and generates summary.
6+
"""
7+
8+
import json
9+
import sys
10+
from pathlib import Path
11+
12+
13+
def main():
14+
results_base = Path("tests/results")
15+
16+
print("==> Consolidating results from all evaluations")
17+
18+
# Find all summary.json files (artifacts download to flat structure)
19+
summary_files = sorted(results_base.glob("*/summary.json"))
20+
21+
if not summary_files:
22+
print(f"No results found in {results_base}")
23+
print(f"Directory contents: {list(results_base.glob('*'))}")
24+
# Write empty comment so job doesn't fail
25+
Path("comment.md").write_text("# Evaluation Results\n\nNo results found.\n")
26+
sys.exit(0)
27+
28+
# Check for failures
29+
failed = 0
30+
succeeded = 0
31+
all_results = []
32+
33+
print("\nResults:")
34+
print("========")
35+
36+
for summary_file in summary_files:
37+
try:
38+
summary = json.loads(summary_file.read_text())
39+
artifact_name = summary_file.parent.name
40+
41+
all_results.append({
42+
"artifact": artifact_name,
43+
"summary": summary,
44+
})
45+
46+
print(f"✅ {artifact_name}")
47+
succeeded += 1
48+
49+
except json.JSONDecodeError:
50+
print(f"❌ {summary_file.parent.name} - Could not parse JSON")
51+
failed += 1
52+
53+
print("\nSummary:")
54+
print("========")
55+
print(f"Processed: {len(all_results)}")
56+
print(f"Failed: {failed}")
57+
58+
# Generate comment
59+
comment = "# 📊 Evaluation Results\n\n"
60+
61+
if all_results:
62+
comment += f"Processed {len(all_results)} evaluation(s).\n\n"
63+
64+
# Build table with results
65+
comment += "| Test Name | Model | Baseline | With Skill | Cases Pass | Winner |\n"
66+
comment += "|-----------|-------|----------|------------|------------|--------|\n"
67+
68+
# Rating hierarchy for comparison
69+
rating_hierarchy = {'vague': 0, 'regular': 1, 'good': 2, 'outstanding': 3}
70+
71+
for result in all_results:
72+
summary = result['summary']
73+
artifact = result['artifact']
74+
75+
# Debug: Log top-level keys
76+
print(f"\n📋 Processing: {artifact}")
77+
print(f" Top-level keys: {list(summary.keys())}")
78+
79+
# Extract key data from nested results[0]
80+
eval_result = summary.get('results', [{}])[0] if summary.get('results') else {}
81+
print(f" Nested result keys: {list(eval_result.keys())}")
82+
83+
skill = eval_result.get('skill', 'N/A')
84+
model = eval_result.get('model', 'N/A')
85+
baseline_rating = eval_result.get('baseline_rating', 'N/A')
86+
skill_rating = eval_result.get('skill_rating', 'N/A')
87+
baseline_pass = eval_result.get('baseline_pass_count', 'N/A')
88+
skill_pass = eval_result.get('skill_pass_count', 'N/A')
89+
overall_better = eval_result.get('judgment', {}).get('overall_better', 'N/A')
90+
91+
print(f" Extracted: model={model}, baseline={baseline_rating}, skill={skill_rating}, winner={overall_better}")
92+
93+
# Determine winner
94+
if overall_better == 'A':
95+
winner = "Baseline"
96+
elif overall_better == 'B':
97+
winner = "With Skill"
98+
elif overall_better == 'TIE':
99+
winner = "Tie"
100+
else:
101+
winner = "N/A"
102+
103+
# Determine emoji for cases pass (skill >= baseline in rating hierarchy)
104+
baseline_score = rating_hierarchy.get(baseline_rating, -1)
105+
skill_score = rating_hierarchy.get(skill_rating, -1)
106+
pass_emoji = "✅" if skill_score >= baseline_score else "❌"
107+
108+
# Build row
109+
test_link = f"[{artifact}]()"
110+
comment += f"| {test_link} | {model} | {baseline_rating} | {skill_rating} | {pass_emoji} {skill_pass} | {winner} |\n"
111+
112+
comment += "\n"
113+
else:
114+
comment += "No evaluation results found.\n"
115+
116+
Path("comment.md").write_text(comment)
117+
print("\n✓ Comment saved to comment.md")
118+
119+
sys.exit(0)
120+
121+
122+
if __name__ == "__main__":
123+
import os
124+
main()

0 commit comments

Comments
 (0)