Files
ai-inference/src/main.ts

126 lines
3.6 KiB
TypeScript
Raw Normal View History

2025-04-04 07:27:58 +11:00
import * as core from '@actions/core'
import ModelClient, { isUnexpected } from '@azure-rest/ai-inference'
import { AzureKeyCredential } from '@azure/core-auth'
import * as fs from 'fs'
import * as os from 'os'
import * as path from 'path'
const RESPONSE_FILE = 'modelResponse.txt'
2025-04-04 07:27:58 +11:00
2025-05-25 19:08:36 +02:00
/**
* Helper function to load content from a file or use fallback input
* @param filePathInput - Input name for the file path
* @param contentInput - Input name for the direct content
* @param defaultValue - Default value to use if neither file nor content is provided
* @returns The loaded content
*/
function loadContentFromFileOrInput(
filePathInput: string,
contentInput: string,
defaultValue?: string
): string {
const filePath = core.getInput(filePathInput)
const contentString = core.getInput(contentInput)
if (filePath !== undefined && filePath !== '') {
if (!fs.existsSync(filePath)) {
throw new Error(`File for ${filePathInput} was not found: ${filePath}`)
}
return fs.readFileSync(filePath, 'utf-8')
} else if (contentString !== undefined && contentString !== '') {
return contentString
} else if (defaultValue !== undefined) {
return defaultValue
} else {
throw new Error(`Neither ${filePathInput} nor ${contentInput} was set`)
}
}
2025-04-04 07:27:58 +11:00
/**
* The main function for the action.
*
* @returns Resolves when the action is complete.
*/
export async function run(): Promise<void> {
try {
2025-05-25 19:08:36 +02:00
// Load prompt content - required
const prompt = loadContentFromFileOrInput(
'prompt-file',
'prompt'
)
2025-05-25 19:08:36 +02:00
// Load system prompt with default value
const systemPrompt = loadContentFromFileOrInput(
'system-prompt-file',
'system-prompt',
'You are a helpful assistant'
)
feat: Add system-prompt-file input for file-based system prompts This enhancement adds the ability to load a system prompt from a file, similar to the existing prompt-file functionality, providing more flexibility when working with complex system prompts. Key changes: - Added new `system-prompt-file` input to action.yml with proper description - Updated main.ts implementation to handle file-based system prompts with: - File existence checking and appropriate error handling - Proper precedence (system-prompt-file takes priority over system-prompt) - Consistent error messages with existing prompt-file implementation Test coverage added: - Basic usage: Test verifies system-prompt-file loads content correctly - Error handling: Test ensures proper errors when system-prompt-file doesn't exist - Precedence: Test confirms system-prompt-file overrides system-prompt when both exist - Integration: Test validates both prompt-file and system-prompt-file work together - Max tokens: Test verifies custom token limits are properly passed to the model - Testing infrastructure: Improved mock implementations that detect unexpected calls Documentation: - Updated README.md with system-prompt-file in inputs table - Added dedicated usage example for system-prompt-file - Fixed formatting in inputs table CI/CD: - Updated workflow to test system-prompt-file functionality with actual file content This feature maintains backward compatibility while adding a useful option for workflows that need to use more complex system prompts stored in files.
2025-05-24 03:50:15 +02:00
2025-04-07 00:09:42 +00:00
const modelName: string = core.getInput('model')
2025-04-07 04:53:19 +00:00
const maxTokens: number = parseInt(core.getInput('max-tokens'), 10)
2025-04-04 07:27:58 +11:00
2025-04-07 04:53:19 +00:00
const token = core.getInput('token') || process.env['GITHUB_TOKEN']
if (token === undefined) {
throw new Error('GITHUB_TOKEN is not set')
}
2025-04-08 04:55:27 +00:00
2025-04-08 20:22:47 +00:00
const endpoint = core.getInput('endpoint')
2025-04-04 07:27:58 +11:00
2025-04-10 02:53:54 +00:00
const client = ModelClient(endpoint, new AzureKeyCredential(token), {
userAgentOptions: { userAgentPrefix: 'github-actions-ai-inference' }
})
const response = await client.path('/chat/completions').post({
body: {
messages: [
{
role: 'system',
2025-04-07 04:53:19 +00:00
content: systemPrompt
},
{ role: 'user', content: prompt }
],
2025-04-07 00:09:42 +00:00
max_tokens: maxTokens,
model: modelName
}
})
if (isUnexpected(response)) {
2025-04-07 23:52:33 +00:00
if (response.body.error) {
throw response.body.error
}
throw new Error(
'An error occurred while fetching the response (' +
response.status +
'): ' +
response.body
)
}
const modelResponse: string | null =
response.body.choices[0].message.content
2025-04-04 07:27:58 +11:00
// Set outputs for other workflow steps to use
core.setOutput('response', modelResponse || '')
// Save the response to a file in case the response overflow the output limit
const responseFilePath = path.join(tempDir(), RESPONSE_FILE)
2025-04-17 20:23:07 +00:00
core.setOutput('response-file', responseFilePath)
if (modelResponse && modelResponse !== '') {
fs.writeFileSync(responseFilePath, modelResponse, 'utf-8')
}
2025-04-04 07:27:58 +11:00
} catch (error) {
// Fail the workflow run if an error occurs
2025-04-07 23:52:33 +00:00
if (error instanceof Error) {
core.setFailed(error.message)
} else {
core.setFailed('An unexpected error occurred')
}
2025-04-04 07:27:58 +11:00
}
}
function tempDir(): string {
const tempDirectory = process.env['RUNNER_TEMP'] || os.tmpdir()
return tempDirectory
}