From 3b72058127ad5808875df9e88478e12fd83112e3 Mon Sep 17 00:00:00 2001 From: Olivier Cots Date: Mon, 7 Sep 2026 23:10:09 +0200 Subject: [PATCH 1/2] Add reusable Albert agent workflow. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .github/workflows/albert-hello.yml | 92 ++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 .github/workflows/albert-hello.yml diff --git a/.github/workflows/albert-hello.yml b/.github/workflows/albert-hello.yml new file mode 100644 index 0000000..aefa4f7 --- /dev/null +++ b/.github/workflows/albert-hello.yml @@ -0,0 +1,92 @@ +name: Albert Hello + +on: + workflow_call: + inputs: + handbook_ref: + description: "Handbook branch, tag, or commit containing the agent" + required: false + type: string + default: main + agent_path: + description: "Path to the agent instructions in Handbook" + required: false + type: string + default: agents-ai/albert-hello.md + model: + description: "Albert model alias or model identifier" + required: false + type: string + default: deepseek + prompt: + description: "Prompt sent to the agent" + required: true + type: string + secrets: + ALBERT_API_KEY: + required: true + HANDBOOK_READ_TOKEN: + required: true + +permissions: + contents: read + +jobs: + call: + name: Call Albert agent + runs-on: ubuntu-latest + steps: + - name: Checkout agent instructions + uses: actions/checkout@v6 + with: + repository: control-toolbox/Handbook + ref: ${{ inputs.handbook_ref }} + path: handbook + token: ${{ secrets.HANDBOOK_READ_TOKEN }} + sparse-checkout: ${{ inputs.agent_path }} + sparse-checkout-cone-mode: false + + - name: Resolve model + id: model + env: + MODEL_INPUT: ${{ inputs.model }} + shell: bash + run: | + case "$MODEL_INPUT" in + deepseek) + echo "model=deepseek-ai/DeepSeek-V4-Flash" >> "$GITHUB_OUTPUT" + ;; + *) + echo "model=$MODEL_INPUT" >> "$GITHUB_OUTPUT" + ;; + esac + + - name: Call Albert API + env: + ALBERT_API_KEY: ${{ secrets.ALBERT_API_KEY }} + AGENT_PATH: handbook/${{ inputs.agent_path }} + MODEL: ${{ steps.model.outputs.model }} + PROMPT: ${{ inputs.prompt }} + shell: bash + run: | + set -euo pipefail + + test -f "$AGENT_PATH" + agent_instructions=$(cat "$AGENT_PATH") + payload=$(jq -n \ + --arg model "$MODEL" \ + --arg system "$agent_instructions" \ + --arg prompt "$PROMPT" \ + '{model: $model, messages: [ + {role: "system", content: $system}, + {role: "user", content: $prompt} + ]}') + + response=$(curl --fail-with-body --silent --show-error \ + --request POST \ + --url "https://albert.api.etalab.gouv.fr/v1/chat/completions" \ + --header "Authorization: Bearer $ALBERT_API_KEY" \ + --header "Content-Type: application/json" \ + --data "$payload") + + printf '%s\n' "$response" | jq -r '.choices[0].message.content // error("Albert response has no message content")' From 3af3fdbe4d29a23b4993561b29d412a738a2a799 Mon Sep 17 00:00:00 2001 From: Olivier Cots Date: Mon, 7 Sep 2026 23:23:08 +0200 Subject: [PATCH 2/2] Add generic AI provider workflow. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../{albert-hello.yml => ai-agent.yml} | 53 +++++++++++++++---- 1 file changed, 42 insertions(+), 11 deletions(-) rename .github/workflows/{albert-hello.yml => ai-agent.yml} (63%) diff --git a/.github/workflows/albert-hello.yml b/.github/workflows/ai-agent.yml similarity index 63% rename from .github/workflows/albert-hello.yml rename to .github/workflows/ai-agent.yml index aefa4f7..16b769b 100644 --- a/.github/workflows/albert-hello.yml +++ b/.github/workflows/ai-agent.yml @@ -1,20 +1,25 @@ -name: Albert Hello +name: AI Agent on: workflow_call: inputs: + provider: + description: "AI provider to use" + required: false + type: string + default: albert handbook_ref: description: "Handbook branch, tag, or commit containing the agent" required: false type: string default: main - agent_path: - description: "Path to the agent instructions in Handbook" + agent_name: + description: "Agent name under Handbook/ai-agents" required: false type: string - default: agents-ai/albert-hello.md + default: hello model: - description: "Albert model alias or model identifier" + description: "Provider model alias or model identifier" required: false type: string default: deepseek @@ -24,7 +29,7 @@ on: type: string secrets: ALBERT_API_KEY: - required: true + required: false HANDBOOK_READ_TOKEN: required: true @@ -33,9 +38,34 @@ permissions: jobs: call: - name: Call Albert agent + name: Call AI agent runs-on: ubuntu-latest steps: + - name: Validate provider and agent name + env: + PROVIDER: ${{ inputs.provider }} + AGENT_NAME: ${{ inputs.agent_name }} + shell: bash + run: | + set -euo pipefail + + case "$PROVIDER" in + albert) + ;; + *) + echo "Unsupported provider: $PROVIDER" >&2 + echo "This workflow currently implements the albert provider." >&2 + exit 1 + ;; + esac + + case "$AGENT_NAME" in + ""|*..*|*/*) + echo "agent_name must be a non-empty file name without path separators or '..'." >&2 + exit 1 + ;; + esac + - name: Checkout agent instructions uses: actions/checkout@v6 with: @@ -43,7 +73,7 @@ jobs: ref: ${{ inputs.handbook_ref }} path: handbook token: ${{ secrets.HANDBOOK_READ_TOKEN }} - sparse-checkout: ${{ inputs.agent_path }} + sparse-checkout: ai-agents/${{ inputs.agent_name }}.md sparse-checkout-cone-mode: false - name: Resolve model @@ -61,16 +91,17 @@ jobs: ;; esac - - name: Call Albert API + - name: Call AI provider env: ALBERT_API_KEY: ${{ secrets.ALBERT_API_KEY }} - AGENT_PATH: handbook/${{ inputs.agent_path }} + AGENT_PATH: handbook/ai-agents/${{ inputs.agent_name }}.md MODEL: ${{ steps.model.outputs.model }} PROMPT: ${{ inputs.prompt }} shell: bash run: | set -euo pipefail + test -n "$ALBERT_API_KEY" test -f "$AGENT_PATH" agent_instructions=$(cat "$AGENT_PATH") payload=$(jq -n \ @@ -89,4 +120,4 @@ jobs: --header "Content-Type: application/json" \ --data "$payload") - printf '%s\n' "$response" | jq -r '.choices[0].message.content // error("Albert response has no message content")' + printf '%s\n' "$response" | jq -r '.choices[0].message.content // error("AI provider response has no message content")'