v1.2.8 premium diet generation endpoint

This commit is contained in:
bossanyit 2023-05-11 15:37:52 +02:00
parent 7a6dd693cb
commit e3182c210c
11 changed files with 113 additions and 30 deletions

View File

@ -11,7 +11,7 @@ plugins {
} }
group = "com.aitrainer" group = "com.aitrainer"
version = "1.2.7" version = "1.2.8"
java.sourceCompatibility = JavaVersion.VERSION_17 java.sourceCompatibility = JavaVersion.VERSION_17
repositories { repositories {

5
data/db/update_1_2_8.sql Normal file
View File

@ -0,0 +1,5 @@
START TRANSACTION;
UPDATE configuration set config_value = "1.2.8", date_change=CURRENT_DATE WHERE config_key = "db_version";
COMMIT;

View File

@ -1,11 +1,24 @@
package com.aitrainer.api.controller.diet package com.aitrainer.api.controller.diet
import com.aallam.openai.api.BetaOpenAI
import com.aallam.openai.api.chat.ChatMessage
import com.aallam.openai.api.chat.ChatRole
import com.aitrainer.api.model.OpenAIChat
import com.aitrainer.api.model.diet.Diet import com.aitrainer.api.model.diet.Diet
import com.aitrainer.api.openai.OpenAIService
import com.aitrainer.api.repository.diet.DietRepository import com.aitrainer.api.repository.diet.DietRepository
import com.google.gson.Gson
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.http.MediaType import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.* import org.springframework.web.bind.annotation.*
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.* import java.util.*
@RestController @RestController
@ -58,4 +71,56 @@ class DietController(private val dietRepository: DietRepository) {
return if (list.isEmpty()) ResponseEntity.notFound().build() else return if (list.isEmpty()) ResponseEntity.notFound().build() else
ResponseEntity.ok().body(list) ResponseEntity.ok().body(list)
} }
@OptIn(BetaOpenAI::class, DelicateCoroutinesApi::class)
@PostMapping("/diet/generate_premium/{dietUserId}", produces = [MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8"])
fun generatePremiumDiet(@PathVariable dietUserId: Long, @RequestBody input: String, @Value("\${openai.key}") openaiKey: String): ResponseEntity<*> {
val systemMsg = ChatMessage(
role = ChatRole.System,
content = "Te egy táplálkozási szakértő vagy"
)
val userMsg = ChatMessage(
role = ChatRole.User,
content = input
)
val listMessages: MutableList<ChatMessage> = mutableListOf()
listMessages.add(systemMsg)
listMessages.add(userMsg)
val gson = Gson()
val messages = gson.toJson(listMessages)
val openai = OpenAIChat(
messages = messages,
modelName = "gpt-4",
temperature = 0.9
)
var result: String? = null
val openAIService = OpenAIService(openaiKey, openai.modelName, openai.temperature)
val deferred = GlobalScope.async {
println(openai.messages)
openAIService.chatCompletion(openai.messages)
}
runBlocking {
try {
result = deferred.await().toString()
} catch (exception: Exception) {
println("Timeout for diet generation $dietUserId")
}
}
if ( result == null ) {
return ResponseEntity.badRequest().build<String>()
}
val now = LocalDateTime.now()
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
val todayString = now.format(formatter)
val diet: Diet = Diet(
dietUserId = dietUserId,
dietText = result!!,
startDate = todayString,
premium = 1
)
return ResponseEntity.ok().body(dietRepository.save(diet))
}
} }

View File

@ -30,7 +30,7 @@ class OpenAIService(@Value("\${openai.key}") private val openaiKey: String, priv
val config = OpenAIConfig( val config = OpenAIConfig(
token = openaiKey, token = openaiKey,
logLevel = LogLevel.All, logLevel = LogLevel.All,
timeout = Timeout(socket = 300.seconds) timeout = Timeout(socket = 600.seconds)
) )
openAI = OpenAI(config) openAI = OpenAI(config)
modelId = ModelId(modelName) modelId = ModelId(modelName)

View File

@ -16,7 +16,7 @@ logging.config=classpath:logback-spring.xml
logging.file=logs logging.file=logs
# if the database structure has been changed, increment this version number # if the database structure has been changed, increment this version number
application.version=1.2.7 application.version=1.2.8
jwt.secret=aitrainer jwt.secret=aitrainer

View File

@ -14,7 +14,7 @@ logging.config=classpath:logback-spring.xml
logging.file=logs logging.file=logs
# if the database structue has been changed, increment this version number # if the database structue has been changed, increment this version number
application.version=1.2.7 application.version=1.2.8
jwt.secret=aitrainer jwt.secret=aitrainer

View File

@ -1,7 +0,0 @@
#spring.config.activate.on-profile=dev,test,prod,prodtest
spring.config.use-legacy-processing = true
## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url = jdbc:mysql://192.168.100.98:3306/diet4you?serverTimezone=CET&useSSL=false&characterEncoding=UTF-8&allowMultiQueries=true
spring.datasource.username = aitrainer
spring.datasource.password = ENC(WZplPYr8WmrLHshesY4T6oXplK3MlUVJ)
spring.http.encoding.charset=UTF-8

View File

@ -14,7 +14,7 @@ logging.config=classpath:logback-spring.xml
logging.file=logs logging.file=logs
# if the database structue has been changed, increment this version number # if the database structue has been changed, increment this version number
application.version=1.2.7 application.version=1.2.8
jwt.secret=aitrainer jwt.secret=aitrainer

View File

@ -1,14 +0,0 @@
spring.config.activate.on-profile=testmac
spring.config.use-legacy-processing = true
## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
#spring.datasource.url = jdbc:mysql://localhost:3306/aitrainer?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false
spring.datasource.url = jdbc:mysql://127.0.0.1:3306/aitrainer2?serverTimezone=CET&useSSL=false&characterEncoding=UTF-8&allowPublicKeyRetrieval=true&allowMultiQueries=true
spring.datasource.username = root
spring.datasource.password = ENC(WZplPYr8WmrLHshesY4T6oXplK3MlUVJ)
## Hibernate Properties
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQLDialect

View File

@ -17,7 +17,7 @@ logging.config=classpath:logback-spring.xml
logging.file=logs logging.file=logs
# if the database structure has been changed, increment this version number # if the database structure has been changed, increment this version number
application.version=1.2.7 application.version=1.2.8
jwt.secret=aitrainer jwt.secret=aitrainer

View File

@ -3,7 +3,6 @@ package com.aitrainer.api.test.diet
import com.aitrainer.api.model.diet.Diet import com.aitrainer.api.model.diet.Diet
import com.aitrainer.api.model.diet.DietMeal import com.aitrainer.api.model.diet.DietMeal
import com.aitrainer.api.repository.diet.DietRepository import com.aitrainer.api.repository.diet.DietRepository
import com.aitrainer.api.repository.diet.MealRepository
import com.aitrainer.api.test.Tokenizer import com.aitrainer.api.test.Tokenizer
import com.google.gson.Gson import com.google.gson.Gson
import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.BeforeAll
@ -158,14 +157,49 @@ class DietTest {
.andExpect(jsonPath("$.premium").value(1)) .andExpect(jsonPath("$.premium").value(1))
.andReturn() .andReturn()
val gson2= Gson()
val newDietJson2 = mvcResult2.response.contentAsString val newDietJson2 = mvcResult2.response.contentAsString
val newDiet2 = gson.fromJson(newDietJson, Diet::class.java) val newDiet2 = gson.fromJson(newDietJson2, Diet::class.java)
dietRepository.delete(newDiet) dietRepository.delete(newDiet)
dietRepository.delete(newDiet2) dietRepository.delete(newDiet2)
} }
//@Test
fun `generate premium diet successfully`() {
val input = "Készíts egy személyre szabott heti étrendet ezekkel a paraméterekkel:\n" +
"Férfi, 50 éves, 120kg, 170 magas, célja fogyás.\n" +
"- Speciális étrend: ketogén\n" +
"- Allergiák: olajos magvak\n" +
"- Preferált ételek: magyar konyha\n" +
"- Ezek az ételek nem szerepelhetnek az étrendben: főzelékek\n" +
"- napi kalóriacél: 2100 kCal\n" +
"- Étel frekvencia: reggeli, ebéd, uzsonna, vacsora\n" +
"- Ebéd és vacsora megegyezik. \n" +
"- ne legyen ismétlődés az ételeknél\n" +
"\n" +
"A választ ebben a formátumban add meg:\n" +
"{ \"DIET\": [\n" +
"{\"nameDay\": enum<monday, tuesday, wednesday...>\n" +
"\"mealTime\":enum<breakfast, lunch+dinner, snack>\n" +
"\"meals\":[<mealNames in Hungarian>],\n" +
"\"quantities\":[<quantities for each mealName in gramms>],\n" +
"\"calories\":[<calorie values for each mealName>],\n" +
"}, \"sumCal\":double\n" +
"]}"
mockMvc.perform(
MockMvcRequestBuilders.post("/api/diet/generate_premium/2")
.contentType(MediaType.APPLICATION_JSON)
.header("Authorization", "Bearer $authToken")
.content(input)
)
.andExpect(status().isOk)
.andExpect(jsonPath("$.dietUserId").value(2))
.andExpect(jsonPath("$.premium").value(1))
}
private fun toJson(obj: Any): String { private fun toJson(obj: Any): String {
return Gson().toJson(obj) return Gson().toJson(obj)
} }