Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/test-action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,25 @@ jobs:
uses: ./
with:
prompt-file: '.github/prompts/ai.md'

test-action-structured-output:
name: Test Action with Structured Output
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Test action with schema
id: structured-test
uses: ./
with:
prompt: 'Provide a simple test response'
response-schema-file: '__tests__/test-schema.json'

- name: Test message field is not empty
run: |
MESSAGE="${{ fromJSON(steps.structured-test.outputs.text).message }}"
if [ -z "$MESSAGE" ]; then
echo "❌ Test failed: message field is empty"
exit 1
else
echo "✅ Test passed: message field is not empty (length: ${#MESSAGE})"
fi
58 changes: 51 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ A GitHub Action that lets you Prompt AI directly in your workflows.
- [Basic Usage 🚀](#basic-usage-)
- [Provide prompt directly](#provide-prompt-directly)
- [Load a prompt from a file](#load-a-prompt-from-a-file)
- [Get structured outputs (JSON)](#get-structured-outputs-json)
- [Permissions 🔒](#permissions-)
- [Inputs ⚙️](#inputs-️)
- [Outputs 📤](#outputs-)
- [Advanced Usage 🔧](#advanced-usage-)
- [Using system prompts with structured outputs](#using-system-prompts-with-structured-outputs)
- [Cool examples 🎮](#cool-examples-)
- [Respond to Issues](#respond-to-issues)
- [Automatically format PR titles to conventional commits](#automatically-format-pr-titles-to-conventional-commits)
Expand All @@ -35,23 +38,45 @@ A GitHub Action that lets you Prompt AI directly in your workflows.
prompt-file: .github/prompts/my-prompt.md
```

### Get structured outputs (JSON)

Enable structured outputs by providing a JSON Schema file. This ensures
consistent, typed responses from AI models.

```yaml
- name: Get structured response
id: ai-response
uses: FidelusAleksander/prompt-action@v1
with:
prompt: 'Analyze this code and provide feedback'
response-schema-file: '__tests__/code-analysis.json'
```

Learn more about JSON Schema syntax at
[json-schema.org](https://json-schema.org/).

The AI will respond with structured JSON matching your schema.

## Permissions 🔒

This actions requires at minimum the following permissions set.

```
```yaml
permissions:
models: read
```

## Inputs ⚙️

| Input | Description | Required | Default |
| ------------- | --------------------------------------------------------------------------------------- | -------- | --------------------- |
| `prompt` | The text prompt to send to the AI | No\* | - |
| `prompt-file` | Path to a file containing the prompt | No\* | - |
| `token` | Personal access token | No | `${{ github.token }}` |
| `model` | The AI model to use. See [available models](https://github.com/marketplace?type=models) | No | `gpt-4o` |
| Input | Description | Required | Default |
|-------|-------------|----------|---------|
| `prompt` | Text that will be used as user prompt | No* | - |
| `prompt-file` | Path to a file containing the user prompt | No* | - |
| `token` | Personal access token | No | `${{ github.token }}` |
| `model` | The AI model to use. See [available models](https://github.com/marketplace?type=models) | No | `gpt-4o` |
| `system-prompt` | Text that will be used as system prompt | No | "You are a helpful assistant." |
| `system-prompt-file` | Path to a file containing the system prompt | No | - |
| `response-schema-file` | Path to a file containing the response [JSON Schema](https://json-schema.org/implementers/interfaces) for structured outputs | No | - |

\* Either `prompt` or `prompt-file` must be provided

Expand All @@ -61,6 +86,25 @@ permissions:
| ------ | -------------------------------- |
| `text` | The AI's response to your prompt |

## Advanced Usage 🔧

### Using system prompts with structured outputs

Combine system prompts with structured outputs for specialized AI behavior:

```yaml
- name: Code review with structured output
uses: FidelusAleksander/prompt-action@v1
with:
prompt: |
Review this pull request:
${{ github.event.pull_request.body }}
system-prompt: |
You are a senior software engineer performing code reviews.
Focus on security, performance, and maintainability.
response-schema-file: '__tests__/code-review.json'
```

## Cool examples 🎮

Have you come up with a clever use of this action? Open a PR to showcase it here
Expand Down
91 changes: 91 additions & 0 deletions __tests__/ai.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,95 @@ describe('generateAIResponse', () => {
generateAIResponse(mockPrompt, mockSystemPrompt, mockModel, mockToken)
).rejects.toThrow('No response was generated by the AI model')
})

it('should generate AI response with structured output schema', async () => {
const mockResponse = '{"message": "Test response"}'
const schema = {
type: 'object',
properties: {
message: { type: 'string' }
},
required: ['message']
}

openai.mockCreate.mockResolvedValueOnce({
id: 'chatcmpl-123',
object: 'chat.completion',
created: 1677652288,
model: 'gpt-4',
choices: [
{
message: {
content: mockResponse,
refusal: null,
role: 'assistant'
},
finish_reason: 'stop',
index: 0
}
]
})

const response = await generateAIResponse(
mockPrompt,
mockSystemPrompt,
mockModel,
mockToken,
schema
)

expect(response).toBe(mockResponse)
expect(openai.mockCreate).toHaveBeenCalledWith({
model: mockModel,
messages: [
{ role: 'system', content: mockSystemPrompt },
{ role: 'user', content: mockPrompt }
],
response_format: {
type: 'json_schema',
json_schema: {
name: 'structured_response',
schema: schema,
strict: true
}
}
})
})

it('should generate AI response without schema when not provided', async () => {
const mockResponse = 'Test response'
openai.mockCreate.mockResolvedValueOnce({
id: 'chatcmpl-123',
object: 'chat.completion',
created: 1677652288,
model: 'gpt-4',
choices: [
{
message: {
content: mockResponse,
refusal: null,
role: 'assistant'
},
finish_reason: 'stop',
index: 0
}
]
})

const response = await generateAIResponse(
mockPrompt,
mockSystemPrompt,
mockModel,
mockToken
)

expect(response).toBe(mockResponse)
expect(openai.mockCreate).toHaveBeenCalledWith({
model: mockModel,
messages: [
{ role: 'system', content: mockSystemPrompt },
{ role: 'user', content: mockPrompt }
]
})
})
})
36 changes: 36 additions & 0 deletions __tests__/code-analysis.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"type": "object",
"properties": {
"summary": {
"type": "string",
"description": "Brief summary of the analysis"
},
"issues": {
"type": "array",
"items": {
"type": "object",
"properties": {
"severity": {
"type": "string",
"enum": ["low", "medium", "high", "critical"]
},
"description": {
"type": "string"
},
"line": {
"type": "number"
}
},
"required": ["severity", "description"],
"additionalProperties": false
}
},
"score": {
"type": "number",
"minimum": 0,
"maximum": 100
}
},
"required": ["summary", "issues", "score"],
"additionalProperties": false
}
Loading