Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Copy this file to .env and fill in real values.
# Never commit .env to git.

# OLLAMA API key used by Spring property: ollama.apiKey
OLLAMA_API_KEY=
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,8 @@ out/
**/combined.graphql

node_modules/

# Local environment secrets
.env
.env.local
.env.*.local
6 changes: 3 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ repositories {
}

dependencies {
implementation 'de.unistuttgart.iste.meitrex:meitrex-common:1.4.12'
implementation 'de.unistuttgart.iste.meitrex:meitrex-common:1.6.0'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-graphql'
implementation 'org.springframework.boot:spring-boot-starter-validation'
Expand All @@ -119,15 +119,15 @@ dependencies {
implementation 'org.modelmapper:modelmapper:3.+'
implementation 'com.graphql-java:graphql-java-extended-scalars:22.0'
implementation 'com.graphql-java:graphql-java-extended-validation:22.0'
implementation 'de.unistuttgart.iste.meitrex:content_service:1.0.9'
implementation 'de.unistuttgart.iste.meitrex:content_service:1.6.1'
implementation 'com.fasterxml.jackson.module:jackson-module-jsonSchema:2.17.1'
implementation 'io.dapr:dapr-sdk-springboot:1.12.0'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'de.unistuttgart.iste.meitrex:meitrex-common-test:1.4.12'
testImplementation 'de.unistuttgart.iste.meitrex:meitrex-common-test:1.6.0'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework:spring-webflux'
testImplementation 'org.springframework.graphql:spring-graphql-test'
Expand Down
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ services:
- "1301:1301"
depends_on:
- database
env_file:
- ./../tutor_service/.env
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://database:5432/tutor_service
SPRING_DATASOURCE_USERNAME: root
Expand Down
116 changes: 116 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"dependencies": {
"graphql-markdown": "^7.3.0"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ public static void main(String[] args) {
}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package de.unistuttgart.iste.meitrex.tutor_service.config;

import de.unistuttgart.iste.meitrex.common.ollama.OllamaClient;
import de.unistuttgart.iste.meitrex.common.config.OllamaConfig;
import de.unistuttgart.iste.meitrex.common.service.JsonSchemaGeneratorService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.net.http.HttpClient;
import java.time.Duration;

@Configuration
public class OllamaClientConfiguration {

@Bean
public OllamaConfig ollamaConfig() {
return new OllamaConfig();
}

@Bean
public HttpClient ollamaHttpClient() {
return HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(30))
.build();
}

@Bean
public JsonSchemaGeneratorService jsonSchemaGeneratorService() {
return new JsonSchemaGeneratorService();
}

@Bean
public OllamaClient ollamaClient(OllamaConfig config,
JsonSchemaGeneratorService schemaService,
ObjectMapper objectMapper,
HttpClient ollamaHttpClient) {
return new OllamaClient(config, schemaService, objectMapper, ollamaHttpClient);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

import de.unistuttgart.iste.meitrex.common.event.HexadPlayerType;
import de.unistuttgart.iste.meitrex.common.user_handling.LoggedInUser;
import de.unistuttgart.iste.meitrex.common.ollama.OllamaClient;
import de.unistuttgart.iste.meitrex.generated.dto.*;
import de.unistuttgart.iste.meitrex.tutor_service.service.models.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand All @@ -18,7 +20,7 @@
@Service
@RequiredArgsConstructor
public class HintService {
private final OllamaService ollamaService;
private final OllamaClient ollamaClient;
private final SemanticSearchService semanticSearchService;
private final UserPlayerTypeService userPlayerTypeService;

Expand All @@ -41,11 +43,11 @@ public HintResponse generateHintWithQuestion(
// First fill the prompt corresponding to the question type
HintGenerationData generationData = getGenerationData(input);
String promptName = PROMPT_TEMPLATES.get("QUESTION").replace("{QUESTION_TYPE}", input.getType().toString());
String questionPrompt = ollamaService.getTemplate(promptName);
List<TemplateArgs> questionPromptArgs = List.of(
TemplateArgs.builder().argumentName("questionText").argumentValue(generationData.getQuestionText()).build(),
TemplateArgs.builder().argumentName("options").argumentValue(generationData.getOptionsText()).build());
questionPrompt = ollamaService.fillTemplate(questionPrompt, questionPromptArgs);
String questionPrompt = ollamaClient.getTemplate(promptName);
Map<String, String> questionPromptArgs = new HashMap<>();
questionPromptArgs.put("questionText", generationData.getQuestionText());
questionPromptArgs.put("options", generationData.getOptionsText());
questionPrompt = ollamaClient.fillTemplate(questionPrompt, questionPromptArgs);

/*
* Perform a semantic search using the query defined in HintGenerationData
Expand All @@ -72,17 +74,20 @@ public HintResponse generateHintWithQuestion(
* Now generate the hint given the question, answer options (which is injected via the questionPrompt) and
* the text inside segments which have been found relevant via the semantic search
*/
String prompt = ollamaService.getTemplate(PROMPT_TEMPLATES.get("GENERATION"));
String contentString = semanticSearchService.formatIntoNumberedListForPrompt(
documentSegments.stream().map(DocumentRecordSegment::getText).toList());
String gamificationPrompt = generateGamificationPrompt(courseId, currentUser);
List<TemplateArgs> promptArgs = List.of(
TemplateArgs.builder().argumentName("questionPrompt").argumentValue(questionPrompt).build(),
TemplateArgs.builder().argumentName("content").argumentValue(contentString).build(),
TemplateArgs.builder().argumentName("gamificationPrompt").argumentValue(gamificationPrompt).build()
Map<String, String> promptArgs = new HashMap<>();
promptArgs.put("questionPrompt", questionPrompt);
promptArgs.put("content", contentString);
promptArgs.put("gamificationPrompt", gamificationPrompt);

return ollamaClient.startQuery(
HintResponse.class,
PROMPT_TEMPLATES.get("GENERATION"),
promptArgs,
new HintResponse("An error occurred")
);

return ollamaService.startQuery(HintResponse.class, prompt, promptArgs, new HintResponse("An error occurred"));
}

/**
Expand Down Expand Up @@ -160,12 +165,14 @@ private HintGenerationData buildAssociationData(HintAssociationInput input) {
.toList());

// Generate a semantic search query based on the question text and the pairs
List<TemplateArgs> promptArgs = List.of(
TemplateArgs.builder().argumentName("pairs").argumentValue(optionsString).build()
Map<String, String> promptArgs = new HashMap<>();
promptArgs.put("pairs", optionsString);
SemanticSearchQuery semanticSearchQuery = ollamaClient.startQuery(
SemanticSearchQuery.class,
PROMPT_TEMPLATES.get("SEMANTIC_SEARCH_QUERY_ASSOCIATION"),
promptArgs ,
new SemanticSearchQuery(questionText)
);
String promptName = ollamaService.getTemplate(PROMPT_TEMPLATES.get("SEMANTIC_SEARCH_QUERY_ASSOCIATION"));
SemanticSearchQuery semanticSearchQuery = ollamaService.startQuery(
SemanticSearchQuery.class, promptName, promptArgs , new SemanticSearchQuery(questionText));

log.info("Generated search query {}", semanticSearchQuery.getQuery());

Expand All @@ -185,13 +192,15 @@ private HintGenerationData buildClozeData(HintClozeInput input) {
String optionsString = semanticSearchService.formatIntoNumberedListForPrompt(blanks);

// Generate a semantic search query based on the cloze and its corresponding answer options
List<TemplateArgs> promptArgs = List.of(
TemplateArgs.builder().argumentName("clozeText").argumentValue(questionText).build(),
TemplateArgs.builder().argumentName("answers").argumentValue(optionsString).build()
Map<String, String> promptArgs = new HashMap<>();
promptArgs.put("clozeText", questionText);
promptArgs.put("answers", optionsString);
SemanticSearchQuery semanticSearchQuery = ollamaClient.startQuery(
SemanticSearchQuery.class,
PROMPT_TEMPLATES.get("SEMANTIC_SEARCH_QUERY_CLOZE"),
promptArgs,
new SemanticSearchQuery(questionText)
);
String promptName = ollamaService.getTemplate(PROMPT_TEMPLATES.get("SEMANTIC_SEARCH_QUERY_CLOZE"));
SemanticSearchQuery semanticSearchQuery = ollamaService.startQuery(
SemanticSearchQuery.class, promptName, promptArgs , new SemanticSearchQuery(questionText));

log.info("Generated search query {}", semanticSearchQuery.getQuery());

Expand Down
Loading
Loading