Skip to content

Commit efdf724

Browse files
committed
feat(ai-dev): refactor to 3-job harness (Planner / Generator / Evaluator)
1 parent 1ccbb77 commit efdf724

2 files changed

Lines changed: 464 additions & 126 deletions

File tree

.github/actions/gemini/action.yml

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
name: 'Run Gemini with fallback'
2+
description: 'Run gemini-cli against a prompt file, with CLI model fallback and optional direct REST API fallback for plan-style read-only tasks.'
3+
4+
inputs:
5+
api_key:
6+
description: 'Gemini API key (secret).'
7+
required: true
8+
prompt_file:
9+
description: 'Path to a file containing the prompt to send.'
10+
required: true
11+
output_file:
12+
description: 'Path to write Gemini stdout to.'
13+
required: true
14+
models:
15+
description: 'Comma-separated fallback chain (CLI --model values).'
16+
required: true
17+
yolo:
18+
description: 'If "true", pass --yolo so tool calls (file edits) auto-accept. Set "false" for analysis-only callers that should also fail-safe via prompt instruction.'
19+
default: 'true'
20+
allow_rest_fallback:
21+
description: 'If "true", also try a direct Gemini REST API call when every CLI attempt fails. Useful for plan/eval phases that only need text output; pointless for phases that must edit files.'
22+
default: 'false'
23+
24+
outputs:
25+
used_model:
26+
description: 'The model that produced output, or empty if all attempts failed.'
27+
value: ${{ steps.run.outputs.used_model }}
28+
used_rest:
29+
description: 'true if the REST fallback produced the output, false if CLI did.'
30+
value: ${{ steps.run.outputs.used_rest }}
31+
32+
runs:
33+
using: composite
34+
steps:
35+
- name: Install gemini-cli
36+
shell: bash
37+
run: |
38+
if ! command -v gemini >/dev/null 2>&1; then
39+
npm install -g @google/gemini-cli
40+
fi
41+
echo "::group::gemini version"
42+
gemini --version || true
43+
echo "::endgroup::"
44+
45+
- name: Run with fallback
46+
id: run
47+
shell: bash
48+
env:
49+
GEMINI_API_KEY: ${{ inputs.api_key }}
50+
PROMPT_FILE: ${{ inputs.prompt_file }}
51+
OUTPUT_FILE: ${{ inputs.output_file }}
52+
MODELS_CSV: ${{ inputs.models }}
53+
YOLO_FLAG: ${{ inputs.yolo == 'true' && '--yolo' || '' }}
54+
ALLOW_REST: ${{ inputs.allow_rest_fallback }}
55+
GEMINI_CLI_TRUST_WORKSPACE: 'true'
56+
run: |
57+
set -e
58+
mkdir -p "$(dirname "$OUTPUT_FILE")" .ai
59+
60+
rc=1
61+
used_model=""
62+
used_rest="false"
63+
64+
IFS=',' read -ra MODELS <<< "$MODELS_CSV"
65+
for model in "${MODELS[@]}"; do
66+
model="$(echo "$model" | xargs)"
67+
[ -z "$model" ] && continue
68+
echo "::group::CLI attempt: $model"
69+
set +e
70+
gemini --model "$model" $YOLO_FLAG --prompt "$(cat "$PROMPT_FILE")" \
71+
> "$OUTPUT_FILE" 2> .ai/gemini.err
72+
rc=$?
73+
set -e
74+
echo "exit: $rc"
75+
tail -n 20 .ai/gemini.err 2>/dev/null || true
76+
echo "::endgroup::"
77+
78+
if [ $rc -eq 0 ]; then
79+
used_model="$model"
80+
echo "CLI succeeded with $model"
81+
break
82+
fi
83+
if grep -qE 'TerminalQuotaError|Quota exceeded|"code": ?429|status: ?429' .ai/gemini.err 2>/dev/null; then
84+
echo "::notice::$model hit quota; trying next"
85+
continue
86+
fi
87+
echo "::warning::$model failed with non-quota error; stop CLI loop"
88+
break
89+
done
90+
91+
if [ $rc -ne 0 ] && [ "$ALLOW_REST" = "true" ]; then
92+
echo "::notice::falling back to direct Gemini REST API"
93+
for model in "${MODELS[@]}"; do
94+
model="$(echo "$model" | xargs)"
95+
[ -z "$model" ] && continue
96+
echo "::group::REST attempt: $model"
97+
body=$(jq -n --rawfile p "$PROMPT_FILE" '{contents:[{parts:[{text:$p}]}]}')
98+
http_code=$(curl -sS -o .ai/rest.json -w '%{http_code}' \
99+
"https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent" \
100+
-H "x-goog-api-key: $GEMINI_API_KEY" \
101+
-H "Content-Type: application/json" \
102+
-d "$body" || echo "000")
103+
echo "HTTP $http_code"
104+
if [ "$http_code" = "200" ]; then
105+
text=$(jq -r '.candidates[0].content.parts[0].text // empty' .ai/rest.json)
106+
if [ -n "$text" ]; then
107+
printf '%s' "$text" > "$OUTPUT_FILE"
108+
rc=0
109+
used_model="$model"
110+
used_rest="true"
111+
echo "REST succeeded with $model"
112+
echo "::endgroup::"
113+
break
114+
fi
115+
fi
116+
jq -r '.error.message // .' .ai/rest.json 2>/dev/null | head -3 || true
117+
echo "::endgroup::"
118+
done
119+
fi
120+
121+
echo "used_model=$used_model" >> "$GITHUB_OUTPUT"
122+
echo "used_rest=$used_rest" >> "$GITHUB_OUTPUT"
123+
124+
if [ $rc -ne 0 ]; then
125+
echo "::error::all Gemini attempts (CLI${ALLOW_REST:+ + REST}) failed"
126+
echo "::group::stdout tail"
127+
tail -n 60 "$OUTPUT_FILE" 2>/dev/null || echo "(empty)"
128+
echo "::endgroup::"
129+
exit $rc
130+
fi

0 commit comments

Comments
 (0)