Merge pull request #3 from actions/sgoedecke/initial-setup
Setup initial inference flow and install packages
This commit is contained in:
@@ -9,7 +9,7 @@ ACTIONS_STEP_DEBUG=true
|
||||
|
||||
# GitHub Actions inputs should follow `INPUT_<name>` format (case-sensitive).
|
||||
# Hyphens should not be converted to underscores!
|
||||
INPUT_MILLISECONDS=2400
|
||||
INPUT_PROMPT=hello
|
||||
|
||||
# GitHub Actions default environment variables. These are set for every run of a
|
||||
# workflow and can be used in your actions. Setting the value here will override
|
||||
|
||||
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
@@ -10,6 +10,7 @@ on:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
models: read
|
||||
|
||||
jobs:
|
||||
test-typescript:
|
||||
@@ -43,6 +44,8 @@ jobs:
|
||||
- name: Test
|
||||
id: npm-ci-test
|
||||
run: npm run ci-test
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
|
||||
test-action:
|
||||
name: GitHub Actions Test
|
||||
@@ -57,8 +60,10 @@ jobs:
|
||||
id: test-action
|
||||
uses: ./
|
||||
with:
|
||||
milliseconds: 2000
|
||||
prompt: hello
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
|
||||
- name: Print Output
|
||||
id: output
|
||||
run: echo "${{ steps.test-action.outputs.time }}"
|
||||
run: echo "${{ steps.test-action.outputs.response }}"
|
||||
|
||||
12
.github/workflows/licensed.yml
vendored
12
.github/workflows/licensed.yml
vendored
@@ -8,12 +8,12 @@ on:
|
||||
# Uncomment the below lines to run this workflow on pull requests and pushes
|
||||
# to the default branch. This is useful for checking licenses before merging
|
||||
# changes into the default branch.
|
||||
# pull_request:
|
||||
# branches:
|
||||
# - main
|
||||
# push:
|
||||
# branches:
|
||||
# - main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
|
||||
1
.github/workflows/linter.yml
vendored
1
.github/workflows/linter.yml
vendored
@@ -51,3 +51,4 @@ jobs:
|
||||
VALIDATE_TYPESCRIPT_ES: false
|
||||
VALIDATE_JSON: false
|
||||
VALIDATE_TYPESCRIPT_STANDARD: false
|
||||
VALIDATE_GITHUB_ACTIONS: false # false until linter schemas are updated to include the models permission
|
||||
|
||||
78
CODE_OF_CONDUCT.md
Normal file
78
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to make participation in our project and our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and
|
||||
expression, level of experience, education, socio-economic status, nationality,
|
||||
personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
- Using welcoming and inclusive language
|
||||
- Being respectful of differing viewpoints and experiences
|
||||
- Gracefully accepting constructive criticism
|
||||
- Focusing on what is best for the community
|
||||
- Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
- The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, or to ban temporarily or permanently any
|
||||
contributor for other behaviors that they deem inappropriate, threatening,
|
||||
offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all project spaces, and it also applies when
|
||||
an individual is representing the project or its community in public spaces.
|
||||
Examples of representing a project or community include using an official
|
||||
project email address, posting via an official social media account, or acting
|
||||
as an appointed representative at an online or offline event. Representation of
|
||||
a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at `opensource@github.com`. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an
|
||||
incident. Further details of specific enforcement policies may be posted
|
||||
separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 1.4, available at
|
||||
`https://www.contributor-covenant.org/version/1/4/code-of-conduct.html`
|
||||
|
||||
[homepage]: `https://www.contributor-covenant.org`
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
`https://www.contributor-covenant.org/faq`
|
||||
108
CONTRIBUTING.md
Normal file
108
CONTRIBUTING.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# Contributing
|
||||
|
||||
[fork]: https://github.com/actions/ai-inference/fork
|
||||
[pr]: https://github.com/actions/ai-inference/compare
|
||||
[code-of-conduct]: CODE_OF_CONDUCT.md
|
||||
|
||||
Hi there! We're thrilled that you'd like to contribute to this project. Your
|
||||
help is essential for keeping it great.
|
||||
|
||||
Contributions to this project are
|
||||
[released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license)
|
||||
to the public under the [project's open source license](LICENSE).
|
||||
|
||||
Please note that this project is released with a [Contributor Code of
|
||||
Conduct][code-of-conduct]. By participating in this project you agree to abide
|
||||
by its terms.
|
||||
|
||||
## Found a bug?
|
||||
|
||||
- **Ensure the bug was not already reported** by searching on GitHub under
|
||||
[Issues](https://github.com/actions/ai-inference/issues).
|
||||
- If you're unable to find an open issue addressing the problem,
|
||||
[open a new one](https://github.com/actions/ai-inference/issues/new). Be sure
|
||||
to include a **title and clear description**, as much relevant information as
|
||||
possible, and a **code sample** or a **reproducible test case** demonstrating
|
||||
the expected behavior that is not occurring.
|
||||
- If possible, use the relevant bug report templates to create the issue.
|
||||
|
||||
## What should I know before submitting a pull request or issue
|
||||
|
||||
This project is written in [TypeScript](https://www.typescriptlang.org/), a
|
||||
typed variant of JavaScript, and we use [Prettier](https://prettier.io/) to get
|
||||
a consistent code style.
|
||||
|
||||
Because of how GitHub Actions are run, the source code of this project is
|
||||
transpiled from TypeScript into JavaScript. The transpiled code (found in
|
||||
`lib/`) is subsequently compiled using
|
||||
[NCC](https://github.com/vercel/ncc/blob/master/readme.md) (found in `dist/`) to
|
||||
avoid having to include the `node_modules/` directory in the repository.
|
||||
|
||||
## Submitting a pull request
|
||||
|
||||
1. [Fork][fork] and clone the repository
|
||||
1. Configure and install the dependencies: `npm install`
|
||||
1. Create a new branch: `git checkout -b my-branch-name`
|
||||
1. Make your change, add tests, and make sure the tests still pass:
|
||||
`npm run test`
|
||||
1. Make sure your code is correctly formatted: `npm run format`
|
||||
1. Update `dist/index.js` using `npm run build`. This creates a single
|
||||
JavaScript file that is used as an entrypoint for the action
|
||||
1. Push to your fork and [submit a pull request][pr]
|
||||
1. Pat yourself on the back and wait for your pull request to be reviewed and
|
||||
merged.
|
||||
|
||||
Here are a few things you can do that will increase the likelihood of your pull
|
||||
request being accepted:
|
||||
|
||||
- Write tests.
|
||||
- Keep your change as focused as possible. If there are multiple changes you
|
||||
would like to make that are not dependent upon each other, consider submitting
|
||||
them as separate pull requests.
|
||||
|
||||
## Releasing a new version
|
||||
|
||||
All the concepts from
|
||||
[the actions/toolkit release docs](https://github.com/actions/toolkit/blob/main/docs/action-versioning.md)
|
||||
apply. Please read that first!
|
||||
|
||||
Once the changes are merged into main, a repository maintainer should:
|
||||
|
||||
1. Bump the package version by running
|
||||
[`npm version [major|minor|patch]`](https://docs.npmjs.com/cli/v7/commands/npm-version).
|
||||
We adhere to [SemVer 2.0](https://semver.org/spec/v2.0.0.html) to the best of
|
||||
our ability. Commit the changes to `package.json` and `package-lock.json` and
|
||||
push them to main.
|
||||
1. [Draft a new release](https://github.com/actions/ai-inference/releases/new)
|
||||
pointing to the ref of the version bump you just made. Publish the release to
|
||||
the marketplace when complete.
|
||||
1. Finally: update the corresponding "major tag" (v1, v2, v3, etc) to point to
|
||||
the specific ref of the release you just made. For example, if we just
|
||||
released `v1.1.0`, we would rewrite the `v1` tag like this:
|
||||
|
||||
```bash
|
||||
git tag -fa v1 v1.1.0 -m "Update v1 tag to point to v1.1.0"
|
||||
git push origin v1 --force
|
||||
```
|
||||
|
||||
## Licensed
|
||||
|
||||
This repository uses a tool called
|
||||
[Licensed](https://github.com/github/licensed) to verify third party
|
||||
dependencies. You may need to locally install licensed and run `licensed cache`
|
||||
to update the dependency cache if you install or update a production dependency.
|
||||
If licensed cache is unable to determine the dependency, you may need to modify
|
||||
the cache file yourself to put the correct license. You should still verify the
|
||||
dependency, licensed is a tool to help, but is not a substitute for human review
|
||||
of dependencies.
|
||||
|
||||
## Resources
|
||||
|
||||
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
|
||||
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
|
||||
- [GitHub Help](https://help.github.com)
|
||||
- [Writing good commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
|
||||
|
||||
Thanks! :heart: :heart: :heart:
|
||||
|
||||
GitHub Actions Team :octocat:
|
||||
304
README.md
304
README.md
@@ -1,4 +1,4 @@
|
||||
# Create a GitHub Action Using TypeScript
|
||||
# AI Inference in GitHub Actions
|
||||
|
||||
[](https://github.com/super-linter/super-linter)
|
||||

|
||||
@@ -6,238 +6,65 @@
|
||||
[](https://github.com/actions/typescript-action/actions/workflows/codeql-analysis.yml)
|
||||
[](./badges/coverage.svg)
|
||||
|
||||
Use this template to bootstrap the creation of a TypeScript action. :rocket:
|
||||
|
||||
This template includes compilation support, tests, a validation workflow,
|
||||
publishing, and versioning guidance.
|
||||
|
||||
If you are new, there's also a simpler introduction in the
|
||||
[Hello world JavaScript action repository](https://github.com/actions/hello-world-javascript-action).
|
||||
|
||||
## Create Your Own Action
|
||||
|
||||
To create your own action, you can use this repository as a template! Just
|
||||
follow the below instructions:
|
||||
|
||||
1. Click the **Use this template** button at the top of the repository
|
||||
1. Select **Create a new repository**
|
||||
1. Select an owner and name for your new repository
|
||||
1. Click **Create repository**
|
||||
1. Clone your new repository
|
||||
|
||||
> [!IMPORTANT]
|
||||
>
|
||||
> Make sure to remove or update the [`CODEOWNERS`](./CODEOWNERS) file! For
|
||||
> details on how to use this file, see
|
||||
> [About code owners](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners).
|
||||
|
||||
## Initial Setup
|
||||
|
||||
After you've cloned the repository to your local machine or codespace, you'll
|
||||
need to perform some initial setup steps before you can develop your action.
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> You'll need to have a reasonably modern version of
|
||||
> [Node.js](https://nodejs.org) handy (20.x or later should work!). If you are
|
||||
> using a version manager like [`nodenv`](https://github.com/nodenv/nodenv) or
|
||||
> [`fnm`](https://github.com/Schniz/fnm), this template has a `.node-version`
|
||||
> file at the root of the repository that can be used to automatically switch to
|
||||
> the correct version when you `cd` into the repository. Additionally, this
|
||||
> `.node-version` file is used by GitHub Actions in any `actions/setup-node`
|
||||
> actions.
|
||||
|
||||
1. :hammer_and_wrench: Install the dependencies
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
1. :building_construction: Package the TypeScript for distribution
|
||||
|
||||
```bash
|
||||
npm run bundle
|
||||
```
|
||||
|
||||
1. :white_check_mark: Run the tests
|
||||
|
||||
```bash
|
||||
$ npm test
|
||||
|
||||
PASS ./index.test.js
|
||||
✓ throws invalid number (3ms)
|
||||
✓ wait 500 ms (504ms)
|
||||
✓ test runs (95ms)
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
## Update the Action Metadata
|
||||
|
||||
The [`action.yml`](action.yml) file defines metadata about your action, such as
|
||||
input(s) and output(s). For details about this file, see
|
||||
[Metadata syntax for GitHub Actions](https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions).
|
||||
|
||||
When you copy this repository, update `action.yml` with the name, description,
|
||||
inputs, and outputs for your action.
|
||||
|
||||
## Update the Action Code
|
||||
|
||||
The [`src/`](./src/) directory is the heart of your action! This contains the
|
||||
source code that will be run when your action is invoked. You can replace the
|
||||
contents of this directory with your own code.
|
||||
|
||||
There are a few things to keep in mind when writing your action code:
|
||||
|
||||
- Most GitHub Actions toolkit and CI/CD operations are processed asynchronously.
|
||||
In `main.ts`, you will see that the action is run in an `async` function.
|
||||
|
||||
```javascript
|
||||
import * as core from '@actions/core'
|
||||
//...
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
//...
|
||||
} catch (error) {
|
||||
core.setFailed(error.message)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For more information about the GitHub Actions toolkit, see the
|
||||
[documentation](https://github.com/actions/toolkit/blob/master/README.md).
|
||||
|
||||
So, what are you waiting for? Go ahead and start customizing your action!
|
||||
|
||||
1. Create a new branch
|
||||
|
||||
```bash
|
||||
git checkout -b releases/v1
|
||||
```
|
||||
|
||||
1. Replace the contents of `src/` with your action code
|
||||
1. Add tests to `__tests__/` for your source code
|
||||
1. Format, test, and build the action
|
||||
|
||||
```bash
|
||||
npm run all
|
||||
```
|
||||
|
||||
> This step is important! It will run [`rollup`](https://rollupjs.org/) to
|
||||
> build the final JavaScript action code with all dependencies included. If
|
||||
> you do not run this step, your action will not work correctly when it is
|
||||
> used in a workflow.
|
||||
|
||||
1. (Optional) Test your action locally
|
||||
|
||||
The [`@github/local-action`](https://github.com/github/local-action) utility
|
||||
can be used to test your action locally. It is a simple command-line tool
|
||||
that "stubs" (or simulates) the GitHub Actions Toolkit. This way, you can run
|
||||
your TypeScript action locally without having to commit and push your changes
|
||||
to a repository.
|
||||
|
||||
The `local-action` utility can be run in the following ways:
|
||||
|
||||
- Visual Studio Code Debugger
|
||||
|
||||
Make sure to review and, if needed, update
|
||||
[`.vscode/launch.json`](./.vscode/launch.json)
|
||||
|
||||
- Terminal/Command Prompt
|
||||
|
||||
```bash
|
||||
# npx @github/local action <action-yaml-path> <entrypoint> <dotenv-file>
|
||||
npx @github/local-action . src/main.ts .env
|
||||
```
|
||||
|
||||
You can provide a `.env` file to the `local-action` CLI to set environment
|
||||
variables used by the GitHub Actions Toolkit. For example, setting inputs and
|
||||
event payload data used by your action. For more information, see the example
|
||||
file, [`.env.example`](./.env.example), and the
|
||||
[GitHub Actions Documentation](https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables).
|
||||
|
||||
1. Commit your changes
|
||||
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "My first action is ready!"
|
||||
```
|
||||
|
||||
1. Push them to your repository
|
||||
|
||||
```bash
|
||||
git push -u origin releases/v1
|
||||
```
|
||||
|
||||
1. Create a pull request and get feedback on your action
|
||||
1. Merge the pull request into the `main` branch
|
||||
|
||||
Your action is now published! :rocket:
|
||||
|
||||
For information about versioning your action, see
|
||||
[Versioning](https://github.com/actions/toolkit/blob/master/docs/action-versioning.md)
|
||||
in the GitHub Actions toolkit.
|
||||
|
||||
## Validate the Action
|
||||
|
||||
You can now validate the action by referencing it in a workflow file. For
|
||||
example, [`ci.yml`](./.github/workflows/ci.yml) demonstrates how to reference an
|
||||
action in the same repository.
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- name: Checkout
|
||||
id: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Test Local Action
|
||||
id: test-action
|
||||
uses: ./
|
||||
with:
|
||||
milliseconds: 1000
|
||||
|
||||
- name: Print Output
|
||||
id: output
|
||||
run: echo "${{ steps.test-action.outputs.time }}"
|
||||
```
|
||||
|
||||
For example workflow runs, check out the
|
||||
[Actions tab](https://github.com/actions/typescript-action/actions)! :rocket:
|
||||
Use AI models from [GitHub Models](https://github.com/marketplace/models) in
|
||||
your actions.
|
||||
|
||||
## Usage
|
||||
|
||||
After testing, you can create version tag(s) that developers can use to
|
||||
reference different stable versions of your action. For more information, see
|
||||
[Versioning](https://github.com/actions/toolkit/blob/master/docs/action-versioning.md)
|
||||
in the GitHub Actions toolkit.
|
||||
|
||||
To include the action in a workflow in another repository, you can use the
|
||||
`uses` syntax with the `@` symbol to reference a specific branch, tag, or commit
|
||||
hash.
|
||||
Create a workflow to use the AI inference action:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- name: Checkout
|
||||
id: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Test Local Action
|
||||
id: test-action
|
||||
uses: actions/typescript-action@v1 # Commit with the `v1` tag
|
||||
id: inference
|
||||
uses: actions/ai-inference@v1
|
||||
with:
|
||||
milliseconds: 1000
|
||||
prompt: 'Hello!'
|
||||
|
||||
- name: Print Output
|
||||
id: output
|
||||
run: echo "${{ steps.test-action.outputs.time }}"
|
||||
run: echo "${{ steps.test-action.outputs.response }}"
|
||||
```
|
||||
|
||||
## Inputs
|
||||
|
||||
Various inputs are defined in [`action.yml`](action.yml) to let you configure
|
||||
the action:
|
||||
|
||||
| Name | Description | Default |
|
||||
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
|
||||
| `token` | Token to use for inference. Typically the GITHUB_TOKEN secret | `github.token` |
|
||||
| `prompt` | The prompt to send to the model | N/A |
|
||||
| `model` | The model to use for inference. Must be available in the [GitHub Models](https://github.com/marketplace?type=models) catalog | `gpt-4o` |
|
||||
| `endpoint` | The endpoint to use for inference. If you're running this as part of an org, you should probably use the org-specific Models endpoint | `https://models.github.ai/inference` |
|
||||
| `max_tokens` | The max number of tokens to generate | 200 |
|
||||
|
||||
## Outputs
|
||||
|
||||
The AI inference action provides the following outputs:
|
||||
|
||||
| Name | Description |
|
||||
| ---------- | --------------------------- |
|
||||
| `response` | The response from the model |
|
||||
|
||||
## Required Permissions
|
||||
|
||||
In order to run inference with GitHub Models, the GitHub AI inference action
|
||||
requires `models` permissions.
|
||||
|
||||
```yml
|
||||
permissions:
|
||||
contents: read
|
||||
models: read
|
||||
```
|
||||
|
||||
## Publishing a New Release
|
||||
|
||||
This project includes a helper script, [`script/release`](./script/release)
|
||||
designed to streamline the process of tagging and pushing new releases for
|
||||
GitHub Actions.
|
||||
GitHub Actions. For more information, see
|
||||
[Versioning](https://github.com/actions/toolkit/blob/master/docs/action-versioning.md)
|
||||
in the GitHub Actions toolkit.
|
||||
|
||||
GitHub Actions allows users to select a specific version of the action to use,
|
||||
based on release tags. This script simplifies this process by performing the
|
||||
@@ -260,47 +87,6 @@ following steps:
|
||||
to create a new release in GitHub so users can easily reference the new tags
|
||||
in their workflows.
|
||||
|
||||
## Dependency License Management
|
||||
## Contributions
|
||||
|
||||
This template includes a GitHub Actions workflow,
|
||||
[`licensed.yml`](./.github/workflows/licensed.yml), that uses
|
||||
[Licensed](https://github.com/licensee/licensed) to check for dependencies with
|
||||
missing or non-compliant licenses. This workflow is initially disabled. To
|
||||
enable the workflow, follow the below steps.
|
||||
|
||||
1. Open [`licensed.yml`](./.github/workflows/licensed.yml)
|
||||
1. Uncomment the following lines:
|
||||
|
||||
```yaml
|
||||
# pull_request:
|
||||
# branches:
|
||||
# - main
|
||||
# push:
|
||||
# branches:
|
||||
# - main
|
||||
```
|
||||
|
||||
1. Save and commit the changes
|
||||
|
||||
Once complete, this workflow will run any time a pull request is created or
|
||||
changes pushed directly to `main`. If the workflow detects any dependencies with
|
||||
missing or non-compliant licenses, it will fail the workflow and provide details
|
||||
on the issue(s) found.
|
||||
|
||||
### Updating Licenses
|
||||
|
||||
Whenever you install or update dependencies, you can use the Licensed CLI to
|
||||
update the licenses database. To install Licensed, see the project's
|
||||
[Readme](https://github.com/licensee/licensed?tab=readme-ov-file#installation).
|
||||
|
||||
To update the cached licenses, run the following command:
|
||||
|
||||
```bash
|
||||
licensed cache
|
||||
```
|
||||
|
||||
To check the status of cached licenses, run the following command:
|
||||
|
||||
```bash
|
||||
licensed status
|
||||
```
|
||||
Contributions are welcome! See the [Contributor's Guide](CONTRIBUTING.md).
|
||||
|
||||
@@ -7,11 +7,29 @@
|
||||
*/
|
||||
import { jest } from '@jest/globals'
|
||||
import * as core from '../__fixtures__/core.js'
|
||||
import { wait } from '../__fixtures__/wait.js'
|
||||
|
||||
// Mocks should be declared before the module being tested is imported.
|
||||
const mockPost = jest.fn().mockImplementation(() => ({
|
||||
body: {
|
||||
choices: [
|
||||
{
|
||||
message: {
|
||||
content: 'Hello, user!'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}))
|
||||
|
||||
jest.unstable_mockModule('@azure-rest/ai-inference', () => ({
|
||||
default: jest.fn(() => ({
|
||||
path: jest.fn(() => ({
|
||||
post: mockPost
|
||||
}))
|
||||
})),
|
||||
isUnexpected: jest.fn(() => false)
|
||||
}))
|
||||
|
||||
jest.unstable_mockModule('@actions/core', () => core)
|
||||
jest.unstable_mockModule('../src/wait.js', () => ({ wait }))
|
||||
|
||||
// The module being tested should be imported dynamically. This ensures that the
|
||||
// mocks are used in place of any actual dependencies.
|
||||
@@ -20,43 +38,35 @@ const { run } = await import('../src/main.js')
|
||||
describe('main.ts', () => {
|
||||
beforeEach(() => {
|
||||
// Set the action's inputs as return values from core.getInput().
|
||||
core.getInput.mockImplementation(() => '500')
|
||||
|
||||
// Mock the wait function so that it does not actually wait.
|
||||
wait.mockImplementation(() => Promise.resolve('done!'))
|
||||
core.getInput.mockImplementation((name) => {
|
||||
if (name === 'prompt') return 'Hello, AI!'
|
||||
if (name === 'system_prompt') return 'You are a test assistant.'
|
||||
if (name === 'model_name') return 'gpt-4o'
|
||||
return ''
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks()
|
||||
})
|
||||
|
||||
it('Sets the time output', async () => {
|
||||
it('Sets the response output', async () => {
|
||||
await run()
|
||||
|
||||
// Verify the time output was set.
|
||||
expect(core.setOutput).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'time',
|
||||
// Simple regex to match a time string in the format HH:MM:SS.
|
||||
expect.stringMatching(/^\d{2}:\d{2}:\d{2}/)
|
||||
'response',
|
||||
'Hello, user!'
|
||||
)
|
||||
})
|
||||
|
||||
it('Sets a failed status', async () => {
|
||||
// Clear the getInput mock and return an invalid value.
|
||||
core.getInput.mockClear().mockReturnValueOnce('this is not a number')
|
||||
|
||||
// Clear the wait mock and return a rejected promise.
|
||||
wait
|
||||
.mockClear()
|
||||
.mockRejectedValueOnce(new Error('milliseconds is not a number'))
|
||||
// Clear the getInput mock and return an empty prompt
|
||||
core.getInput.mockClear().mockReturnValueOnce('')
|
||||
|
||||
await run()
|
||||
|
||||
// Verify that the action was marked as failed.
|
||||
expect(core.setFailed).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'milliseconds is not a number'
|
||||
)
|
||||
expect(core.setFailed).toHaveBeenNthCalledWith(1, 'prompt is not set')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* Unit tests for src/wait.ts
|
||||
*/
|
||||
import { wait } from '../src/wait.js'
|
||||
|
||||
describe('wait.ts', () => {
|
||||
it('Throws an invalid number', async () => {
|
||||
const input = parseInt('foo', 10)
|
||||
|
||||
expect(isNaN(input)).toBe(true)
|
||||
|
||||
await expect(wait(input)).rejects.toThrow('milliseconds is not a number')
|
||||
})
|
||||
|
||||
it('Waits with a valid number', async () => {
|
||||
const start = new Date()
|
||||
await wait(500)
|
||||
const end = new Date()
|
||||
|
||||
const delta = Math.abs(end.getTime() - start.getTime())
|
||||
|
||||
expect(delta).toBeGreaterThan(450)
|
||||
})
|
||||
})
|
||||
28
action.yml
28
action.yml
@@ -1,23 +1,35 @@
|
||||
name: The name of your action here
|
||||
description: Provide a description here
|
||||
author: Your name or organization here
|
||||
name: 'AI Inference'
|
||||
description: Generate an AI response based on a provided prompt
|
||||
author: 'GitHub'
|
||||
|
||||
# Add your action's branding here. This will appear on the GitHub Marketplace.
|
||||
branding:
|
||||
icon: heart
|
||||
icon: 'play-circle'
|
||||
color: red
|
||||
|
||||
# Define your inputs here.
|
||||
inputs:
|
||||
milliseconds:
|
||||
prompt:
|
||||
description: Your input description here
|
||||
required: true
|
||||
default: '1000'
|
||||
default: ''
|
||||
model:
|
||||
description: The model to use
|
||||
required: false
|
||||
default: 'gpt-4o'
|
||||
endpoint:
|
||||
description: The endpoint to use
|
||||
required: false
|
||||
default: 'https://models.github.ai/inference'
|
||||
max_tokens:
|
||||
description: The maximum number of tokens to generate
|
||||
required: false
|
||||
default: '200'
|
||||
|
||||
# Define your outputs here.
|
||||
outputs:
|
||||
time:
|
||||
description: Your output description here
|
||||
response:
|
||||
description: The response from the model
|
||||
|
||||
runs:
|
||||
using: node20
|
||||
|
||||
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="106" height="20" role="img" aria-label="Coverage: 100%"><title>Coverage: 100%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="106" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="63" height="20" fill="#555"/><rect x="63" width="43" height="20" fill="#4c1"/><rect width="106" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">Coverage</text><text x="325" y="140" transform="scale(.1)" fill="#fff" textLength="530">Coverage</text><text aria-hidden="true" x="835" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="330">100%</text><text x="835" y="140" transform="scale(.1)" fill="#fff" textLength="330">100%</text></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="116" height="20" role="img" aria-label="Coverage: 89.47%"><title>Coverage: 89.47%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="116" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="63" height="20" fill="#555"/><rect x="63" width="53" height="20" fill="#dfb317"/><rect width="116" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">Coverage</text><text x="325" y="140" transform="scale(.1)" fill="#fff" textLength="530">Coverage</text><text aria-hidden="true" x="885" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">89.47%</text><text x="885" y="140" transform="scale(.1)" fill="#fff" textLength="430">89.47%</text></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
6389
dist/index.js
generated
vendored
6389
dist/index.js
generated
vendored
File diff suppressed because it is too large
Load Diff
2
dist/index.js.map
generated
vendored
2
dist/index.js.map
generated
vendored
File diff suppressed because one or more lines are too long
54
package-lock.json
generated
54
package-lock.json
generated
@@ -12,6 +12,9 @@
|
||||
"@actions/core": "^1.11.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@azure-rest/ai-inference": "latest",
|
||||
"@azure/core-auth": "latest",
|
||||
"@azure/core-sse": "latest",
|
||||
"@eslint/compat": "^1.2.7",
|
||||
"@github/local-action": "^3.1.3",
|
||||
"@jest/globals": "^29.7.0",
|
||||
@@ -420,6 +423,44 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure-rest/ai-inference": {
|
||||
"version": "1.0.0-beta.6",
|
||||
"resolved": "https://registry.npmjs.org/@azure-rest/ai-inference/-/ai-inference-1.0.0-beta.6.tgz",
|
||||
"integrity": "sha512-j5FrJDTHu2P2+zwFVe5j2edasOIhqkFj+VkDjbhGkQuOoIAByF0egRkgs0G1k03HyJ7bOOT9BkRF7MIgr/afhw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@azure-rest/core-client": "^2.1.0",
|
||||
"@azure/abort-controller": "^2.1.2",
|
||||
"@azure/core-auth": "^1.9.0",
|
||||
"@azure/core-lro": "^2.7.2",
|
||||
"@azure/core-rest-pipeline": "^1.18.2",
|
||||
"@azure/core-tracing": "^1.2.0",
|
||||
"@azure/logger": "^1.1.4",
|
||||
"tslib": "^2.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure-rest/core-client": {
|
||||
"version": "2.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@azure-rest/core-client/-/core-client-2.3.4.tgz",
|
||||
"integrity": "sha512-AQXtD5VqsoOswDmxQR0YyVkYa1tZ0HyfC/fsqfntYZ7EEgaimfCGN2nAfiN3KXy1F4TfcoByhmtX2bVOO2vAEQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@azure/abort-controller": "^2.0.0",
|
||||
"@azure/core-auth": "^1.3.0",
|
||||
"@azure/core-rest-pipeline": "^1.5.0",
|
||||
"@azure/core-tracing": "^1.0.1",
|
||||
"@azure/core-util": "^1.0.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/abort-controller": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz",
|
||||
@@ -524,6 +565,19 @@
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-sse": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-sse/-/core-sse-2.1.3.tgz",
|
||||
"integrity": "sha512-KSSdIKy8kvWCpYr8Hzpu22j3wcXsVTYE0IlgmI1T/aHvBDsLgV91y90UTfVWnuiuApRLCCVC4gS09ApBGOmYQA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/core-tracing": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.2.0.tgz",
|
||||
|
||||
@@ -40,6 +40,9 @@
|
||||
"@actions/core": "^1.11.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@azure-rest/ai-inference": "latest",
|
||||
"@azure/core-auth": "latest",
|
||||
"@azure/core-sse": "latest",
|
||||
"@eslint/compat": "^1.2.7",
|
||||
"@github/local-action": "^3.1.3",
|
||||
"@jest/globals": "^29.7.0",
|
||||
|
||||
49
src/main.ts
49
src/main.ts
@@ -1,5 +1,6 @@
|
||||
import * as core from '@actions/core'
|
||||
import { wait } from './wait.js'
|
||||
import ModelClient, { isUnexpected } from '@azure-rest/ai-inference'
|
||||
import { AzureKeyCredential } from '@azure/core-auth'
|
||||
|
||||
/**
|
||||
* The main function for the action.
|
||||
@@ -8,18 +9,48 @@ import { wait } from './wait.js'
|
||||
*/
|
||||
export async function run(): Promise<void> {
|
||||
try {
|
||||
const ms: string = core.getInput('milliseconds')
|
||||
const prompt: string = core.getInput('prompt')
|
||||
if (prompt === undefined || prompt === '') {
|
||||
throw new Error('prompt is not set')
|
||||
}
|
||||
|
||||
// Debug logs are only output if the `ACTIONS_STEP_DEBUG` secret is true
|
||||
core.debug(`Waiting ${ms} milliseconds ...`)
|
||||
const systemPrompt: string = core.getInput('system_prompt')
|
||||
const modelName: string = core.getInput('model')
|
||||
const maxTokens: number = parseInt(core.getInput('max_tokens'), 10)
|
||||
|
||||
// Log the current timestamp, wait, then log the new timestamp
|
||||
core.debug(new Date().toTimeString())
|
||||
await wait(parseInt(ms, 10))
|
||||
core.debug(new Date().toTimeString())
|
||||
const token = process.env['GITHUB_TOKEN']
|
||||
if (token === undefined) {
|
||||
throw new Error('GITHUB_TOKEN is not set')
|
||||
}
|
||||
const endpoint = core.getInput('endpoint')
|
||||
|
||||
const client = ModelClient(endpoint, new AzureKeyCredential(token))
|
||||
|
||||
const response = await client.path('/chat/completions').post({
|
||||
body: {
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content: systemPrompt || 'You are a helpful assistant.'
|
||||
},
|
||||
{ role: 'user', content: prompt }
|
||||
],
|
||||
temperature: 1.0,
|
||||
top_p: 1.0,
|
||||
max_tokens: maxTokens,
|
||||
model: modelName
|
||||
}
|
||||
})
|
||||
|
||||
if (isUnexpected(response)) {
|
||||
throw response.body.error
|
||||
}
|
||||
|
||||
const modelResponse: string | null =
|
||||
response.body.choices[0].message.content
|
||||
|
||||
// Set outputs for other workflow steps to use
|
||||
core.setOutput('time', new Date().toTimeString())
|
||||
core.setOutput('response', modelResponse || '')
|
||||
} catch (error) {
|
||||
// Fail the workflow run if an error occurs
|
||||
if (error instanceof Error) core.setFailed(error.message)
|
||||
|
||||
13
src/wait.ts
13
src/wait.ts
@@ -1,13 +0,0 @@
|
||||
/**
|
||||
* Waits for a number of milliseconds.
|
||||
*
|
||||
* @param milliseconds The number of milliseconds to wait.
|
||||
* @returns Resolves with 'done!' after the wait is over.
|
||||
*/
|
||||
export async function wait(milliseconds: number): Promise<string> {
|
||||
return new Promise((resolve) => {
|
||||
if (isNaN(milliseconds)) throw new Error('milliseconds is not a number')
|
||||
|
||||
setTimeout(() => resolve('done!'), milliseconds)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user