From 5211dc62566a36a2992b66217bb4155f5022aa3d Mon Sep 17 00:00:00 2001 From: Rishav Dhar <19497993+rdhar@users.noreply.github.com> Date: Thu, 12 Jun 2025 16:19:16 +0100 Subject: [PATCH 1/3] reusable terraform-based ci workflow Signed-off-by: Rishav Dhar <19497993+rdhar@users.noreply.github.com> --- .github/workflows/ci.yml | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6498416..9fa95ab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,20 +25,21 @@ jobs: persist-credentials: false sparse-checkout: action.yml - - name: Inference request + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + + - name: Run Terraform + id: terraform + continue-on-error: true + run: terraform plan + + - name: AI inference request id: prompt uses: ./ with: - payload: | - model: openai/gpt-4.1-mini - messages: - - role: system - content: You are a helpful assistant - - role: user - content: What is the capital of France - max_tokens: 100 - temperature: 0.9 - top_p: 0.9 + system-prompt: You are a helpful DevOps assistant and expert at debugging Terraform errors. + user-prompt: Troubleshoot the following Terraform output; ${{ steps.terraform.outputs.stderr }} + show-payload: true - name: Echo outputs run: | @@ -49,7 +50,4 @@ jobs: echo "${{ steps.prompt.outputs.response-file }}" echo "response-file contents:" - cat "${{ steps.prompt.outputs.response-file }}" | jq - - echo "payload:" - echo "${{ steps.prompt.outputs.payload }}" + cat "${{ steps.prompt.outputs.response-file }}" From 65e56412c788840a8eec02543a66220664cdfb4c Mon Sep 17 00:00:00 2001 From: Rishav Dhar <19497993+rdhar@users.noreply.github.com> Date: Thu, 12 Jun 2025 16:20:09 +0100 Subject: [PATCH 2/3] support all body parameters of the inference request api and package within json payload in order to properly escape special characters Signed-off-by: Rishav Dhar <19497993+rdhar@users.noreply.github.com> --- action.yml | 201 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 167 insertions(+), 34 deletions(-) diff --git a/action.yml b/action.yml index 5fdc321..dcb17bf 100644 --- a/action.yml +++ b/action.yml @@ -1,36 +1,106 @@ --- -name: AI Inference Request via GitHub Action +name: Prompt GitHub AI Models via GitHub Action author: Rishav Dhar (https://rdhar.dev) -description: AI inference request GitHub Models via this GitHub Action. +description: Prompt GitHub AI Models using inference request via GitHub Action API. inputs: - github-api-version: - default: "2022-11-28" - description: GitHub API version (e.g., `2022-11-28`) + # Common inputs. + model: + default: openai/gpt-4.1-mini + description: Model ID to use for the inference request. (e.g., `openai/gpt-4.1-mini`) required: false - github-token: - default: "${{ github.token }}" - description: GitHub token (e.g., `github.token`) + system-prompt: + default: You are a helpful technical assistant, experienced in software engineering CI/CD workflows. Respond concisely, to the point, and in a single response without requesting subsequent user input. + description: Prompt associated with the `system` role. (e.g., `You are a helpful software engineering assistant`) + required: false + user-prompt: + default: List best practices for workflows with GitHub Actions. + description: Prompt associated with the `user` role. (e.g., `List best practices for workflows with GitHub Actions`) + required: false + max-tokens: + default: "" + description: The maximum number of tokens to generate in the completion. The token count of your prompt plus `max-tokens` cannot exceed the model's context length. (e.g., `100`) + required: false + temperature: + default: "" + description: The sampling temperature to use that controls the apparent creativity of generated completions. Higher values will make output more random while lower values will make results more focused and deterministic. (e.g., range is `[0, 1]`) + required: false + top-p: + default: "" + description: An alternative to sampling with temperature called nucleus sampling. This value causes the model to consider the results of tokens with the provided probability mass. (e.g., range is `[0, 1]`) + required: false + + # Additional inputs. + frequency-penalty: + default: "" + description: A value that influences the probability of generated tokens appearing based on their cumulative frequency in generated text. (e.g., range is `[-2, 2]`) + required: false + modalities: + default: "" + description: The modalities that the model is allowed to use for the chat completions response. (e.g., from `text` and `audio`) required: false org: default: "" - description: Organization for request attribution (e.g., `github.repository_owner`) + description: Organization to which the request is to be attributed. (e.g., `github.repository_owner`) + required: false + presence-penalty: + default: "" + description: A value that influences the probability of generated tokens appearing based on their existing presence in generated text. (e.g., range is `[-2, 2]`) + required: false + response-format-schema: + default: "" + description: Desired format for structured JSON response. (TODO) + seed: + default: "" + description: If specified, the system will make a best effort to sample deterministically such that repeated requests with the same seed and parameters should return the same result. (e.g., `123456789`) + required: false + stop: + default: "" + description: A collection of textual sequences that will end completion generation. (e.g., `["\n\n", "END"]`) required: false + stream: + default: "" + description: A value indicating whether chat completions should be streamed for this request. (e.g., `false`) + required: false + stream-include-usage: + default: "" + description: Whether to include usage information in the response. (e.g., `false`) + required: false + tool-choice: + default: "" + description: If specified, the model will configure which of the provided tools it can use for the chat completions response. (e.g., 'auto', 'required', or 'none') + required: false + tools: + default: "" + description: A list of tools the model may request to call. (TODO) + required: false + + # Payload inputs. payload: default: "" - description: Body parameters of the inference request in YAML format (e.g., `model…`) + description: Body parameters of the inference request in JSON format. (e.g., `{"model"…`) required: false payload-file: default: "" - description: Path to a file containing the body parameters of the inference request (e.g., `./payload.{json,yml}`) + description: Path to a JSON file containing the body parameters of the inference request. (e.g., `./payload.json`) required: false show-payload: - default: "true" - description: Whether to show the payload in the logs (e.g., `true`) + default: false + description: Whether to show the body parameters in the workflow log. (e.g., `false`) required: false show-response: - default: "true" - description: Whether to show the response content in the logs (e.g., `true`) + default: true + description: Whether to show the response content in the workflow log. (e.g., `true`) + required: false + + # GitHub API inputs. + github-api-version: + default: "2022-11-28" + description: GitHub API version. (e.g., `2022-11-28`) + required: false + github-token: + default: ${{ github.token }} + description: GitHub token for authorization. (e.g., `github.token`) required: false runs: @@ -39,9 +109,32 @@ runs: - id: request shell: bash env: + # Common inputs. + MODEL: ${{ inputs.model }} + SYSTEM_PROMPT: ${{ inputs.system-prompt }} + USER_PROMPT: ${{ inputs.user-prompt }} + MAX_TOKENS: ${{ inputs.max-tokens }} + TEMPERATURE: ${{ inputs.temperature }} + TOP_P: ${{ inputs.top-p }} + + # Additional inputs. + FREQUENCY_PENALTY: ${{ inputs.frequency-penalty }} + MODALITIES: ${{ inputs.modalities }} + ORG: ${{ inputs.org != '' && format('orgs/{0}/', inputs.org) || '' }} + PRESENCE_PENALTY: ${{ inputs.presence-penalty }} + RESPONSE_FORMAT_SCHEMA: ${{ inputs.response-format-schema }} + SEED: ${{ inputs.seed }} + STOP: ${{ inputs.stop }} + STREAM: ${{ inputs.stream }} + STREAM_INCLUDE_USAGE: ${{ inputs.stream-include-usage }} + TOOL_CHOICE: ${{ inputs.tool-choice }} + TOOLS: ${{ inputs.tools }} + + # GitHub API inputs. API_VERSION: ${{ inputs.github-api-version }} GH_TOKEN: ${{ inputs.github-token }} - ORG: ${{ inputs.org != '' && format('orgs/{0}/', inputs.org) || '' }} + + # Payload inputs. PAYLOAD: ${{ inputs.payload }} PAYLOAD_FILE: ${{ inputs.payload-file }} SHOW_PAYLOAD: ${{ inputs.show-payload }} @@ -54,21 +147,63 @@ runs: echo "Error: Payload file '$PAYLOAD_FILE' does not exist." >&2 exit 1 fi - # Determine whether the format is JSON (starts with '{') or YAML (default) - first_char=$(sed -n 's/^[[:space:]]*\(.\).*/\1/p; q' "$PAYLOAD_FILE") - if [[ "$first_char" == '{' ]]; then - body=$(cat "$PAYLOAD_FILE") - else - body=$(yq --output-format json "$PAYLOAD_FILE") - fi + body=$(cat "$PAYLOAD_FILE") + elif [[ -n "$PAYLOAD" ]]; then + body=$(echo "$PAYLOAD") else - body=$(echo "$PAYLOAD" | yq --output-format json) + body=$(jq --null-input \ + --arg model "$MODEL" \ + --arg system_prompt "$SYSTEM_PROMPT" \ + --arg user_prompt "$USER_PROMPT" \ + --arg max_tokens "$MAX_TOKENS" \ + --arg temperature "$TEMPERATURE" \ + --arg top_p "$TOP_P" \ + --arg frequency_penalty "$FREQUENCY_PENALTY" \ + --arg modalities "$MODALITIES" \ + --arg org "$ORG" \ + --arg presence_penalty "$PRESENCE_PENALTY" \ + --arg response_format_schema "$RESPONSE_FORMAT_SCHEMA" \ + --arg seed "$SEED" \ + --arg stop "$STOP" \ + --arg stream "$STREAM" \ + --arg stream_include_usage "$STREAM_INCLUDE_USAGE" \ + --arg tool_choice "$TOOL_CHOICE" \ + --arg tools "$TOOLS" \ + '{ + model: $model, + messages: [ + { + role: "system", + content: $system_prompt + }, + { + role: "user", + content: $user_prompt + } + ], + max_tokens: $max_tokens, + temperature: $temperature, + top_p: $top_p, + frequency_penalty: $frequency_penalty, + modalities: ($modalities | split(",")), + org: $org, + presence_penalty: $presence_penalty, + response_format_schema: $response_format_schema, + seed: $seed, + stop: ($stop | split(",")), + stream: $stream, + stream_include_usage: $stream_include_usage, + tool_choice: $tool_choice, + tools: ($tools | split(",")) + }' + ) fi - echo "payload_json=$(echo $body)" >> $GITHUB_OUTPUT + body=$(echo "$body" | jq --compact-output --exit-status 'del(.[] | select(. == "" or . == [] or . == {} or . == null))') if [[ "${SHOW_PAYLOAD,,}" == "true" ]]; then echo "$body"; fi # Create a temporary file to store the response temp_file=$(mktemp) + echo "response_file=$temp_file" >> "$GITHUB_OUTPUT" # Send the AI inference request via GitHub API curl \ @@ -79,19 +214,17 @@ runs: --header "Authorization: Bearer $GH_TOKEN" \ --header "Content-Type: application/json" \ --header "X-GitHub-Api-Version: $API_VERSION" \ - --data "$(echo $body | jq --compact-output --exit-status)" \ + --data "$(echo "$body")" \ &> "$temp_file" - # In addition to the temporary file containing the full response, - # return the first 2**18 bytes of the response content (GitHub's limit) - echo "response_file=$temp_file" >> $GITHUB_OUTPUT - echo "response=$(cat $temp_file | jq --raw-output '.choices[0].message.content' | head --bytes 262144 --silent)" >> $GITHUB_OUTPUT - if [[ "${SHOW_RESPONSE,,}" == "true" ]]; then cat "$temp_file" | jq --raw-output '.choices[0].message.content' || true; fi + # Return the first 2**18 bytes of the response content per GitHub's limit + delimiter=$(openssl rand -hex 8) + echo "response<<$delimiter" >> "$GITHUB_OUTPUT" + cat $temp_file | jq --raw-output '.choices[0].message.content' | head --bytes 262144 >> "$GITHUB_OUTPUT" + echo "$delimiter" >> "$GITHUB_OUTPUT" + if [[ "${SHOW_RESPONSE,,}" == "true" ]]; then cat $temp_file | jq --raw-output '.choices[0].message.content' || true; fi outputs: - payload: - description: Body parameters of the inference request in JSON format. - value: ${{ steps.request.outputs.payload_json }} response: description: Response content from the inference request. value: ${{ steps.request.outputs.response }} From 9c0ee192bb1f5ac41fe23d905b4e9ab63d41fef2 Mon Sep 17 00:00:00 2001 From: Rishav Dhar <19497993+rdhar@users.noreply.github.com> Date: Thu, 12 Jun 2025 16:20:24 +0100 Subject: [PATCH 3/3] update docs to reflect the name change as well as input parameters Signed-off-by: Rishav Dhar <19497993+rdhar@users.noreply.github.com> --- README.md | 134 +++++++++++++++++++++++++++++++++++++--------------- SECURITY.md | 2 +- 2 files changed, 97 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 2c68580..73a792d 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -[![GitHub license](https://img.shields.io/github/license/op5dev/ai-inference-request?logo=apache&label=License)](LICENSE "Apache License 2.0.") -[![GitHub release tag](https://img.shields.io/github/v/release/op5dev/ai-inference-request?logo=semanticrelease&label=Release)](https://github.com/op5dev/ai-inference-request/releases "View all releases.") +[![GitHub license](https://img.shields.io/github/license/op5dev/prompt-ai?logo=apache&label=License)](LICENSE "Apache License 2.0.") +[![GitHub release tag](https://img.shields.io/github/v/release/op5dev/prompt-ai?logo=semanticrelease&label=Release)](https://github.com/op5dev/prompt-ai/releases "View all releases.") * -[![GitHub repository stargazers](https://img.shields.io/github/stars/op5dev/ai-inference-request)](https://github.com/op5dev/ai-inference-request "Become a stargazer.") +[![GitHub repository stargazers](https://img.shields.io/github/stars/op5dev/prompt-ai)](https://github.com/op5dev/prompt-ai "Become a stargazer.") -# AI Inference Request via GitHub Action +# Prompt GitHub AI Models via GitHub Action > [!TIP] -> [AI inference request](https://docs.github.com/en/rest/models/inference?apiVersion=2022-11-28#run-an-inference-request "GitHub API documentation.") GitHub Models via this [GitHub Action](https://github.com/marketplace/actions/ai-inference-request-via-github-action "GitHub Actions marketplace."). +> Prompt GitHub AI Models using [inference request](https://docs.github.com/en/rest/models/inference?apiVersion=2022-11-28#run-an-inference-request "GitHub API documentation.") via GitHub Action API.
@@ -14,6 +14,8 @@ [Compare available AI models](https://docs.github.com/en/copilot/using-github-copilot/ai-models/choosing-the-right-ai-model-for-your-task "Comparison of AI models for GitHub.") to choose the best one for your use-case. +### Summarize GitHub Issues + ```yml on: issues: @@ -30,18 +32,13 @@ jobs: steps: - name: Summarize issue id: prompt - uses: op5dev/ai-inference-request@v2 + uses: op5dev/prompt-ai@v2 with: - payload: | - model: openai/gpt-4.1-mini - messages: - - role: system - content: You are a helpful assistant running within GitHub CI. - - role: user - content: Concisely summarize this GitHub issue titled ${{ github.event.issue.title }}: ${{ github.event.issue.body }} - max_tokens: 100 - temperature: 0.9 - top_p: 0.9 + user-prompt: | + Concisely summarize the GitHub issue + with title '${{ github.event.issue.title }}' + and body: ${{ github.event.issue.body }} + max_tokens: 250 - name: Comment summary run: gh issue comment $NUMBER --body "$SUMMARY" @@ -51,31 +48,92 @@ jobs: SUMMARY: ${{ steps.prompt.outputs.response }} ``` +### Troubleshoot Terraform Deployments + +```yml +on: + pull_request: + push: + branches: main + +jobs: + provision: + runs-on: ubuntu-latest + + permissions: + actions: read + checks: write + contents: read + pull-requests: write + models: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + + - name: Provision Terraform + id: provision + uses: op5dev/tf-via-pr@v13 + with: + working-directory: env/dev + command: ${{ github.event_name == 'push' && 'apply' || 'plan' }} + + - name: Troubleshoot Terraform + if: failure() + uses: op5dev/prompt-ai@v2 + with: + model: openai/gpt-4.1-mini + system-prompt: You are a helpful DevOps assistant and expert at debugging Terraform errors. + user-prompt: Troubleshoot the following Terraform output; ${{ steps.provision.outputs.result }} + max-tokens: 500 + temperature: 0.7 + top_p: 0.9 +``` +
## Inputs -Either `payload` or `payload-file` is required with at least `model` and `messages` parameters, per [documentation](https://docs.github.com/en/rest/models/inference?apiVersion=2022-11-28#run-an-inference-request "GitHub API documentation."). - -| Type | Name | Description | -| ------ | -------------------- | ----------------------------------------------------------------------------------------------------------- | -| Data | `payload` | Body parameters of the inference request in YAML format.
Example: `model…` | -| Data | `payload-file` | Path to a file containing the body parameters of the inference request.
Example: `./payload.{json,yml}` | -| Config | `show-payload` | Whether to show the payload in the logs.
Default: `true` | -| Config | `show-response` | Whether to show the response content in the logs.
Default: `true` | -| Admin | `github-api-version` | GitHub API version.
Default: `2022-11-28` | -| Admin | `github-token` | GitHub token.
Default: `github.token` | -| Admin | `org` | Organization for request attribution.
Example: `github.repository_owner` | +The only required input is `user-prompt`, while every parameter can be tuned per [documentation](https://docs.github.com/en/rest/models/inference?apiVersion=2022-11-28#run-an-inference-request "GitHub API documentation."). + +| Type | Name | Description | +| ---------- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Common | `model` | Model ID to use for the inference request.
(e.g., `openai/gpt-4.1-mini`) | +| Common | `system-prompt` | Prompt associated with the `system` role.
(e.g., `You are a helpful software engineering assistant`) | +| Common | `user-prompt` | Prompt associated with the `user` role.
(e.g., `List best practices for workflows with GitHub Actions`) | +| Common | `max-tokens` | The maximum number of tokens to generate in the completion. The token count of your prompt plus `max-tokens` cannot exceed the model's context length.
(e.g., `100`) | +| Common | `temperature` | The sampling temperature to use that controls the apparent creativity of generated completions. Higher values will make output more random while lower values will make results more focused and deterministic.
(e.g., range is `[0, 1]`) | +| Common | `top-p` | An alternative to sampling with temperature called nucleus sampling. This value causes the model to consider the results of tokens with the provided probability mass.
(e.g., range is `[0, 1]`) | +| Additional | `frequency-penalty` | A value that influences the probability of generated tokens appearing based on their cumulative frequency in generated text.
(e.g., range is `[-2, 2]`) | +| Additional | `modalities` | The modalities that the model is allowed to use for the chat completions response.
(e.g., from `text` and `audio`) | +| Additional | `org` | Organization to which the request is to be attributed.
(e.g., `github.repository_owner`) | +| Additional | `presence-penalty` | A value that influences the probability of generated tokens appearing based on their existing presence in generated text.
(e.g., range is `[-2, 2]`) | +| Additional | `seed` | If specified, the system will make a best effort to sample deterministically such that repeated requests with the same seed and parameters should return the same result.
(e.g., `123456789`) | +| Additional | `stop` | A collection of textual sequences that will end completion generation.
(e.g., `["\n\n", "END"]`) | +| Additional | `stream` | A value indicating whether chat completions should be streamed for this request.
(e.g., `false`) | +| Additional | `stream-include-usage` | Whether to include usage information in the response.
(e.g., `false`) | +| Additional | `tool-choice` | If specified, the model will configure which of the provided tools it can use for the chat completions response.
(e.g., 'auto', 'required', or 'none') | +| Payload | `payload` | Body parameters of the inference request in JSON format.
(e.g., `{"model"…`) | +| Payload | `payload-file` | Path to a JSON file containing the body parameters of the inference request.
(e.g., `./payload.json`) | +| Payload | `show-payload` | Whether to show the body parameters in the workflow log.
(e.g., `false`) | +| Payload | `show-response` | Whether to show the response content in the workflow log.
(e.g., `true`) | +| GitHub | `github-api-version` | GitHub API version.
(e.g., `2022-11-28`) | +| GitHub | `github-token` | GitHub token for authorization.
(e.g., `github.token`) |
## Outputs -| Name | Description | -| --------------- | -------------------------------------------------------- | -| `response` | Response content from the inference request. | -| `response-file` | File path containing the complete, raw response. | -| `payload` | Body parameters of the inference request in JSON format. | +Due to GitHub's API limitations, the `response` content is truncated to 262,144 (2^18) characters so the complete, raw response is saved to `response-file`. + +| Name | Description | +| --------------- | --------------------------------------------------------------- | +| `response` | Response content from the inference request. | +| `response-file` | File path containing the complete, raw response in JSON format. | +| `payload` | Body parameters of the inference request in JSON format. |
@@ -91,21 +149,21 @@ View [security policy and reporting instructions](SECURITY.md). ## Changelog -View [all notable changes](https://github.com/op5dev/ai-inference-request/releases "Releases.") to this project in [Keep a Changelog](https://keepachangelog.com "Keep a Changelog.") format, which adheres to [Semantic Versioning](https://semver.org "Semantic Versioning."). +View [all notable changes](https://github.com/op5dev/prompt-ai/releases "Releases.") to this project in [Keep a Changelog](https://keepachangelog.com "Keep a Changelog.") format, which adheres to [Semantic Versioning](https://semver.org "Semantic Versioning."). > [!TIP] > > All forms of **contribution are very welcome** and deeply appreciated for fostering open-source projects. > -> - [Create a PR](https://github.com/op5dev/ai-inference-request/pulls "Create a pull request.") to contribute changes you'd like to see. -> - [Raise an issue](https://github.com/op5dev/ai-inference-request/issues "Raise an issue.") to propose changes or report unexpected behavior. -> - [Open a discussion](https://github.com/op5dev/ai-inference-request/discussions "Open a discussion.") to discuss broader topics or questions. -> - [Become a stargazer](https://github.com/op5dev/ai-inference-request/stargazers "Become a stargazer.") if you find this project useful. +> - [Create a PR](https://github.com/op5dev/prompt-ai/pulls "Create a pull request.") to contribute changes you'd like to see. +> - [Raise an issue](https://github.com/op5dev/prompt-ai/issues "Raise an issue.") to propose changes or report unexpected behavior. +> - [Open a discussion](https://github.com/op5dev/prompt-ai/discussions "Open a discussion.") to discuss broader topics or questions. +> - [Become a stargazer](https://github.com/op5dev/prompt-ai/stargazers "Become a stargazer.") if you find this project useful.
## License - This project is licensed under the **permissive** [Apache License 2.0](LICENSE "Apache License 2.0."). -- All works herein are my own, shared of my own volition, and [contributors](https://github.com/op5dev/ai-inference-request/graphs/contributors "Contributors."). +- All works herein are my own, shared of my own volition, and [contributors](https://github.com/op5dev/prompt-ai/graphs/contributors "Contributors."). - Copyright 2016-present [Rishav Dhar](https://rdhar.dev "Rishav Dhar's profile.") — All wrongs reserved. diff --git a/SECURITY.md b/SECURITY.md index 9bf76b9..fb1cec1 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -17,4 +17,4 @@ Integrating security in your CI/CD pipeline is critical to practicing DevSecOps. ## Reporting a Vulnerability -You must never report security related issues, vulnerabilities or bugs including sensitive information to the issue tracker, or elsewhere in public. Instead, sensitive bugs must be sent by email to or reported via [Security Advisory](https://github.com/op5dev/ai-inference-request/security/advisories/new "Create a new security advisory."). +You must never report security related issues, vulnerabilities or bugs including sensitive information to the issue tracker, or elsewhere in public. Instead, sensitive bugs must be sent by email to or reported via [Security Advisory](https://github.com/op5dev/prompt-ai/security/advisories/new "Create a new security advisory.").