Skip to content

Commit

Permalink
feat: now a assistent is created if none is provided as a env (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
JoeldelPilar authored Dec 12, 2024
1 parent e1c82a3 commit 3d9eb5c
Show file tree
Hide file tree
Showing 6 changed files with 287 additions and 53 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"@nextui-org/react": "^2.4.6",
"@sinclair/typebox": "^0.29.0",
"@tabler/icons-react": "^3.19.0",
"fastify": "4.23.2",
"fastify": "^4.23.2",
"ioredis": "^5.4.1",
"next": "^14.2.15",
"nodemon": "^3.1.7",
Expand Down
2 changes: 1 addition & 1 deletion src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ const createReview: FastifyPluginCallback<ReviewOptions> = (
const { githubUrl } = request.body;
try {
const review = await generateReview(githubUrl);
reply.send({ review });
reply.send({ review: review.review });
} catch (error) {
if (error instanceof Error) {
reply
Expand Down
2 changes: 1 addition & 1 deletion src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export default function Page() {
</form>
</CardBody>
<CardFooter className="text-sm italic">
Please remember that the AI can make misstakes and falty asumptions
Please remember that the AI can make misstakes and falty assumptions
</CardFooter>
</Card>
{isLoading ? <Spinner /> : <Review review={review} error={err} />}
Expand Down
162 changes: 120 additions & 42 deletions src/service/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,48 +9,126 @@ export type repositoryReviewResponse = Static<
typeof GithubRepositoryReviewSchema
>;

export const ReviewSchema = Type.Object({
review: Type.Object({
metadata: Type.Object({
repository_name: Type.String(),
creator: Type.String(),
last_commit_date: Type.String({ format: 'date-time' }),
stars: Type.Integer(),
forks: Type.Integer(),
contributors: Type.Integer()
}),
scoring_criteria: Type.Object({
code_quality: Type.Object({
score: Type.Integer(),
feedback: Type.String()
}),
security: Type.Object({
score: Type.Integer(),
vulnerabilities: Type.Array(
Type.Object({
dependency: Type.String(),
version: Type.String(),
issue: Type.String()
})
export const ReviewSchema = Type.Object(
{
review: Type.Object(
{
metadata: Type.Object(
{
repository_name: Type.String({
description: 'The name of the repository.'
}),
creator: Type.String({
description: 'The creator of the repository.'
}),
last_commit_date: Type.String({
description: 'The date when the last commit was made.'
}),
stars: Type.Number({
description: 'The number of starsgazers the repository have.'
}),
forks: Type.Number({
description: 'The number of forks of the repository.'
}),
contributors: Type.Number({
description:
'The number of people who have contributed to the repository.'
})
},
{ additionalProperties: false }
),
feedback: Type.String()
}),
documentation: Type.Object({
score: Type.Integer(),
feedback: Type.String()
}),
project_structure_and_testing: Type.Object({
score: Type.Integer(),
feedback: Type.String()
}),
version_control_and_git_practices: Type.Object({
score: Type.Integer(),
feedback: Type.String()
}),
overall_score: Type.Integer()
}),
suggestions_for_improvement: Type.Array(Type.String())
})
});
scoring_criteria: Type.Object(
{
code_quality: Type.Object(
{
score: Type.Number({
description: 'The score achieved in code quality.'
}),
feedback: Type.String({
description: 'Feedback regarding code quality.'
})
},
{ additionalProperties: false }
),
security: Type.Object(
{
score: Type.Number({
description: 'The score achieved in security assessment.'
}),
vulnerabilities: Type.Array(
Type.Object(
{
dependency: Type.String({
description: 'Name of the dependency.'
}),
version: Type.String({
description: 'Version of the dependency.'
}),
issue: Type.String({
description: 'Description of the issue in that version.'
})
},
{ additionalProperties: false }
)
),
feedback: Type.String({
description: 'Feedback regarding security practices.'
})
},
{ additionalProperties: false }
),
documentation: Type.Object(
{
score: Type.Number({
description: 'The score achieved for documentation quality.'
}),
feedback: Type.String({
description: 'Feedback regarding documentation completeness.'
})
},
{ additionalProperties: false }
),
project_structure_and_testing: Type.Object(
{
score: Type.Number({
description:
'The score achieved for project structure and testing.'
}),
feedback: Type.String({
description:
'Feedback regarding project structure and testing coverage.'
})
},
{ additionalProperties: false }
),
version_control_and_git_practices: Type.Object(
{
score: Type.Number({
description:
'The score achieved for version control and Git practices.'
}),
feedback: Type.String({
description: 'Feedback regarding version control practices.'
})
},
{ additionalProperties: false }
),
overall_score: Type.Number({
description:
'The overall score calculated based on all the score categories.'
})
},
{ additionalProperties: false }
),
suggestions_for_improvement: Type.Array(Type.String(), {
description:
'List of actionable suggestions to improve the repository.'
})
},
{ additionalProperties: false }
)
},
{ additionalProperties: false }
);

export type ReviewResponse = Static<typeof ReviewSchema>;
47 changes: 39 additions & 8 deletions src/service/models/openai.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,45 @@
import OpenAI from 'openai';
import { TextContentBlock } from 'openai/resources/beta/threads/messages';
import { ReviewSchema } from '../models';
import { REVIEW_PROMPT } from './review_prompt';

const OPENAI_API_KEY =
process.env.OPENAI_API_KEY || 'DID_NOT_SET_OPENAI_API_KEY';

const openai = new OpenAI({ apiKey: OPENAI_API_KEY });
const myAssistantName = process.env.OPENAI_REVIEW_ASST;

export enum ThreadType {
OSAAS_REVIEWER = 'osaas_reviewer'
}

export const Assistants = {
[ThreadType.OSAAS_REVIEWER]:
process.env.OPENAI_REVIEW_ASST || 'asst_WMThAHiv8huPr2SBAeLX34rm'
} as const;
// Function to create or get assistant
async function getOrCreateAssistant() {
const assistants = await openai.beta.assistants.list();
const existingAssistant = assistants.data.find(
(a) => a.name === myAssistantName
);

if (existingAssistant) {
return existingAssistant.id;
}

const assistant = await openai.beta.assistants.create({
name: 'Code Review Assistant',
instructions: REVIEW_PROMPT,
model: 'gpt-4o-mini',
response_format: {
type: 'json_schema',
json_schema: {
name: 'Code_Review_Schema',
schema: ReviewSchema,
strict: true
}
}
});

return assistant.id;
}

function getLatestAssistantMessage(
messages: OpenAI.Beta.Threads.Messages.Message[]
Expand Down Expand Up @@ -43,20 +69,25 @@ async function runAssistant(
});
}

export async function generateReview(githubUrl: string) {
export async function generateReview(
githubUrl: string
): Promise<typeof ReviewSchema> {
const assistantId = await getOrCreateAssistant();
const threadId = (await openai.beta.threads.create()).id;

await openai.beta.threads.messages.create(threadId, {
role: 'user',
content: githubUrl
content: `Please review this GitHub repository: ${githubUrl}`
});

await runAssistant(Assistants[ThreadType.OSAAS_REVIEWER], threadId);
await runAssistant(assistantId, threadId);

const messages = await openai.beta.threads.messages.list(threadId);
const review = getLatestAssistantMessage(messages.data);

if (!review) {
throw new Error(`[openai:generateReview(${githubUrl})] No review found`);
}
return JSON.parse(review);
const response: typeof ReviewSchema = JSON.parse(review);
return response;
}
Loading

0 comments on commit 3d9eb5c

Please sign in to comment.