hugex-gh / app /lib /hugex-service.server.ts
drbh
fix: improve token rention
c202a37
import { accountLinkingService } from './account-linking.server';
import { githubApp } from './github-app.server';
// Default API URL
const DEFAULT_API_URL = 'https://huggingface-hugex-explore.hf.space/api';
interface HugExRepository {
url: string;
}
interface HugExEnvironment {
LLM_MODEL: string;
LLM_PROVIDER: string;
}
interface HugExSecrets {
[key: string]: string;
}
export interface HugExJobCreationParams {
title: string;
description: string;
repository: HugExRepository;
branch: string;
environment: HugExEnvironment;
secrets: HugExSecrets;
}
export interface HugExJob {
id: string;
status: string;
createdAt: string;
title: string;
repository: HugExRepository;
}
export class HugExService {
private apiUrl: string;
constructor() {
this.apiUrl = process.env.HUGEX_API_URL || DEFAULT_API_URL;
// Log configuration status
if (!process.env.HUGEX_API_URL) {
console.log(`ℹ️ Using default HugEx API URL: ${this.apiUrl}`);
} else {
console.log(`βœ… HugEx API configured: ${this.apiUrl}`);
}
}
/**
* Process a GitHub issue to check for @hugex mentions and create jobs
*/
async processGitHubIssue(issue: any, repository: any): Promise<boolean> {
try {
// Check if issue body contains @hugex mention
if (!issue.body || !issue.body.includes('@hugex')) {
return false;
}
console.log(`πŸ€– Found @hugex mention in issue #${issue.number}: ${issue.title}`);
// Get GitHub user's linked HF account
const githubUserId = issue.user.id.toString();
const accountLink = accountLinkingService.findByGitHubUser(githubUserId);
if (!accountLink) {
console.log(`⚠️ No linked HuggingFace account found for GitHub user: ${issue.user.login}`);
// Comment on the issue to inform the user they need to link their account
await this.commentOnIssue(
repository.owner.login,
repository.name,
issue.number,
`⚠️ @${issue.user.login}, to use @hugex, you need to link your GitHub account with your HuggingFace account. Please visit the dashboard to complete this process.`
);
return false;
}
// Extract job parameters from issue body
const jobParams = await this.extractJobParamsFromIssue(issue, repository);
// Create HugEx job
const jobId = await this.createJob(accountLink.huggingfaceUsername, jobParams);
if (jobId) {
console.log(`βœ… Created HugEx job ${jobId} for issue #${issue.number}`);
// Comment on the issue with job info
await this.commentOnIssue(
repository.owner.login,
repository.name,
issue.number,
`πŸš€ Created HugEx job for this issue. You can track progress at ${this.apiUrl}/jobs/${jobId}`
);
return true;
}
return false;
} catch (error) {
console.error('Error processing GitHub issue for HugEx:', error);
return false;
}
}
/**
* Extract job parameters from issue body
*/
private async extractJobParamsFromIssue(issue: any, repository: any): Promise<HugExJobCreationParams> {
// Default values
const params: HugExJobCreationParams = {
title: issue.title,
description: issue.body,
repository: {
url: repository.html_url
},
branch: repository.default_branch || 'main',
environment: {
LLM_MODEL: 'gpt-4',
LLM_PROVIDER: 'openai'
},
secrets: {}
};
// Extract custom parameters if available
try {
// Look for JSON block in the issue body
const jsonMatch = issue.body.match(/```json\s*([\s\S]*?)\s*```/);
if (jsonMatch && jsonMatch[1]) {
const customParams = JSON.parse(jsonMatch[1]);
// Merge with defaults, keeping required structure
if (customParams.title) params.title = customParams.title;
if (customParams.description) params.description = customParams.description;
if (customParams.repository && customParams.repository.url) {
params.repository.url = customParams.repository.url;
}
if (customParams.branch) params.branch = customParams.branch;
if (customParams.environment) params.environment = {
...params.environment,
...customParams.environment
};
if (customParams.secrets) params.secrets = {
...params.secrets,
...customParams.secrets
};
}
} catch (error) {
console.warn('Failed to parse custom parameters from issue body:', error);
// Continue with default parameters
}
return params;
}
/**
* Create a new HugEx job
*/
async createJob(huggingfaceUsername: string, params: HugExJobCreationParams): Promise<string | null> {
try {
// Get HuggingFace token from persistent storage
// Note: In a real implementation, you'd need a secure way to store and retrieve HF tokens
// This is a placeholder - you need to implement token storage
const hfToken = await this.getHuggingFaceToken(huggingfaceUsername);
if (!hfToken) {
console.error(`No HuggingFace token available for user: ${huggingfaceUsername}`);
return null;
}
const response = await fetch(`${this.apiUrl}/jobs/create-with-key`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${hfToken}`
},
body: JSON.stringify(params)
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Failed to create HugEx job: ${response.status} - ${errorText}`);
}
const responseData = await response.json();
return responseData.id || null;
} catch (error) {
console.error('Error creating HugEx job:', error);
return null;
}
}
/**
* Comment on a GitHub issue
*/
private async commentOnIssue(owner: string, repo: string, issueNumber: number, body: string): Promise<void> {
try {
// Get installation Octokit instance for the repository
const installation = await githubApp.getInstallationByRepo(owner, repo);
if (!installation) {
throw new Error(`No GitHub App installation found for repository: ${owner}/${repo}`);
}
const octokit = await githubApp.getInstallationOctokit(installation.id);
await octokit.rest.issues.createComment({
owner,
repo,
issue_number: issueNumber,
body
});
console.log(`βœ… Posted comment on ${owner}/${repo}#${issueNumber}`);
} catch (error) {
console.error('Error commenting on GitHub issue:', error);
}
}
/**
* Get HuggingFace token for a user from the account link
*/
private async getHuggingFaceToken(username: string): Promise<string | null> {
// Look up the user's account link to get the stored token
const accountLink = accountLinkingService.findByHuggingFaceUser(username);
if (accountLink && accountLink.huggingfaceAccessToken) {
console.log(`βœ… Retrieved HuggingFace access token for user: ${username}`);
return accountLink.huggingfaceAccessToken;
}
// Fall back to default token if no specific token is found
console.warn(`⚠️ No HuggingFace access token found for user: ${username}. Using default token if available.`);
return process.env.HF_DEFAULT_TOKEN || null;
}
}
// Singleton instance
export const hugexService = new HugExService();