Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Controler and testing for new endpoint generate-description #1571

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions services/wiki/.env
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ DB_PASS=123456
DB_PORT=10911
DB_USER=postgres
DB_SCHEMA=public

HUGGINGFACE_API_KEY=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
82 changes: 82 additions & 0 deletions services/wiki/src/__tests__/resources/description.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import supertest from 'supertest'
import { describe, it, expect } from 'vitest'
import { server } from '../globalSetup'
import { pathRoot } from '../../routes/routes'
import { authToken } from '../mocks/ssoHandlers/authToken'
import { checkInvalidToken } from '../helpers/checkInvalidToken'

const url: string = `${pathRoot.v1.resources}/generate-description`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

objeto

const urlToTest: string = 'http://www.example.com'
const topic: string = 'Testing topic'
const title: string = 'Test title'

describe('Resources Generate Description', () => {
it('responds with a 200 status code and a description', async () => {
const response = await supertest(server)
.post(`${url}?language=en`)
.set('Cookie', [`authToken=${authToken.admin}`])
.send({ url: urlToTest, topic, title })
expect(response.status).toBe(200)
expect(response.body).toHaveProperty('description')
})
it('should fail is language param is missing', async () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
it('should fail is language param is missing', async () => {
it('should fail if language param is missing', async () => {

const response = await supertest(server)
.post(`${url}`)
.set('Cookie', [`authToken=${authToken.admin}`])
.send({ url: urlToTest, title, topic })

expect(response.status).toBe(400)
expect(response.body.error).toBe('Language parameter is required')
})
it('should fail with missing params (title, url, topic)', async () => {
const response = await supertest(server)
.post(`${url}?language=en`)
.set('Cookie', [`authToken=${authToken.admin}`])
.send({})

expect(response.status).toBe(400)
// expect(response.body.error).toBe('All parameters are required')
})

it('should fail if missing title', async () => {
const response = await supertest(server)
.post(`${url}?language=en`)
.set('Cookie', [`authToken=${authToken.admin}`])
.send({ url: 'http://example.com', topic: 'Testing topic' })

expect(response.status).toBe(400)
// expect(response.body.error).toBe('All parameters are required')
})

it('should fail if missing url', async () => {
const response = await supertest(server)
.post(`${url}?language=en`)
.set('Cookie', [`authToken=${authToken.admin}`])
.send({ title: 'Test Title', topic: 'Testing topic' })

expect(response.status).toBe(400)
// expect(response.body.error).toBe('All parameters are required')
})

it('should fail if missing topic', async () => {
const response = await supertest(server)
.post(`${url}?language=en`)
.set('Cookie', [`authToken=${authToken.admin}`])
.send({ title: 'Test Title', url: 'http://example.com' })

expect(response.status).toBe(400)
expect(response.body.error).toBe('All parameters are required')
})
it('Should return error 401 if no token is provided', async () => {
const response = await supertest(server)
.post(`${pathRoot.v1.resources}`)
.send({ url: urlToTest, title, topic })
expect(response.status).toBe(401)
expect(response.body.message).toBe('Missing token')
})
checkInvalidToken(`${pathRoot.v1.resources}`, 'post', {
url: urlToTest,
title,
topic,
})
})
59 changes: 59 additions & 0 deletions services/wiki/src/controllers/resources/generateDescription.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import Koa, { Middleware } from 'koa'
import { getLanguageInput } from '../../helpers/wiki/getLanguageInput'

export const generateDescription: Middleware = async (ctx: Koa.Context) => {
const { title, url, topic } = ctx.request.body
const { language } = ctx.query

try {
if (!language) {
ctx.status = 400
ctx.body = {
error: 'Language parameter is required',
}
return
}

if (!title || !url || !topic) {
ctx.status = 400
ctx.body = {
error: 'All parameters are required',
}
return
}
const input = getLanguageInput(language as string, title, url, topic)

const response = await fetch(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move to repository pattern

'https://api-inference.huggingface.co/models/Qwen/Qwen2.5-Coder-32B-Instruct',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

env variable

{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.HUGGINGFACE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

env variable

inputs: input,
parameters: {
max_length: 450,
temperature: 0.7,
top_p: 0.95,
},
}),
}
)
if (!response.ok) {
ctx.status = 400
ctx.body = {
error: 'Error fetching data from external API',
}
return
}

const description = await response.json()
ctx.status = 200
ctx.body = { description: description[0]?.generated_text.trim() }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fn unitaria

} catch (error) {
ctx.status = 500
ctx.body = { message: 'An error occured while getting the description' }
}
}
17 changes: 17 additions & 0 deletions services/wiki/src/helpers/wiki/getLanguageInput.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const getLanguageInput = (
language: string,
title: string,
url: string,
topic: string
): string => {
switch (language) {
case 'en':
return `Please provide a detailed summary of the following resource ${title}, including the key points, the main purpose, and the most relevant concepts. Use a clear and accessible tone. The resource can be found at ${url}, and its topic is ${topic}. The summary should be between 200 and 300 words. Return just the summary.`
case 'es':
return `Por favor, proporciona una resumen detallado de la siguiente fuente ${title}, incluyendo los puntos clave, el propósito principal y los conceptos relevantes. Usa un tono claro y accesible. La fuente puede ser encontrada en ${url}, y su tema es ${topic}. El resumen debe estar entre 200 y 300 palabras.`
case 'ca':
return `Si us plau, porporciona un resum detallat de la següent font ${title}, incloent els punts clau, el propòsit principal i els conceptes més rellevants. Empra un to clar i accesible. La font es pot trobar a ${url}, i el seu tema és ${topic}. El resum ha de tenir entre 200 a 300 paraules.`
default:
throw new Error('Unsupported language')
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import z from 'zod'
import { pathRoot } from '../../../routes/routes'
import { cookieAuth } from '../../components/cookieAuth'
import { generateDescriptionSchema } from '../../../schemas/resource/generateDescriptionSchema'
import { registry } from '../../registry'
import { ZodValidationError } from '../../components/errorSchemas'
import { invalidTokenResponse } from '../../components/responses/authMiddleware'

registry.registerPath({
method: 'post',
tags: ['resources'],
path: `${pathRoot.v1.resources}/generate-description`,
operationId: 'postResourcesGenerateDescription',
description:
'Allows an authenticated user to generate a description for a resource. Requires language as a query parameter and title, url, and topic in the request body.',
security: [{ [cookieAuth.name]: [] }],
request: {
query: z.object({
language: z.string().min(2).max(2).optional().openapi({
example: 'es',
}),
}),
body: {
content: {
'application/json': {
schema: generateDescriptionSchema,
},
},
},
},
responses: {
200: {
description: 'A description for the resource is generated.',
content: {
'application/json': {
schema: z.object({
description: z.string(),
}),
},
},
},
400: {
description: 'Error fetching data from external API',
content: {
'application/json': {
schema: z.object({
error: z.string().openapi({
example: 'Error fetching data from external API',
}),
}),
},
},
},
401: invalidTokenResponse,
422: {
description: 'Missing required parameters',
content: {
'application/json': {
schema: ZodValidationError,
},
},
},
500: {
description: 'An error occured while getting the description',
content: {
'application/json': {
schema: z.object({
message: z.string().openapi({
example: 'An error occured while getting the description',
}),
}),
},
},
},
},
})
8 changes: 8 additions & 0 deletions services/wiki/src/routes/resourcesRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { resourceCreateSchema, resourcesListParamsSchema } from '../schemas'
import { pathRoot } from './routes'
import { patchResource } from '../controllers/resources/patchResource'
import { resourcePatchSchema } from '../schemas/resource/resourcePatchSchema'
import { generateDescriptionSchema } from '../schemas/resource/generateDescriptionSchema'
import { generateDescription } from '../controllers/resources/generateDescription'

const resourcesRouter = new Router()

Expand All @@ -23,6 +25,12 @@ resourcesRouter.post(
validate(z.object({ body: resourceCreateSchema })),
postResource
)
resourcesRouter.post(
'/generate-description',
authenticate,
validate(z.object({ body: generateDescriptionSchema })),
generateDescription
)

resourcesRouter.get(
'/',
Expand Down
13 changes: 13 additions & 0 deletions services/wiki/src/schemas/resource/generateDescriptionSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { knexResourceSchema } from './resourceSchema'

export const generateDescriptionSchema = knexResourceSchema.omit({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pick

id: true,
slug: true,
resource_type: true,
category_id: true,
description: true,
created_at: true,
updated_at: true,
user_id: true,
topics: true,
})
Loading