Skip to content
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
48 changes: 48 additions & 0 deletions src/main/java/org/wise/portal/dao/chatbot/ChatDao.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Copyright (c) 2007-2025 Regents of the University of California (Regents).
* Created by WISE, Graduate School of Education, University of California, Berkeley.
*
* This software is distributed under the GNU General Public License, v3,
* or (at your option) any later version.
*
* Permission is hereby granted, without written agreement and without license
* or royalty fees, to use, copy, modify, and distribute this software and its
* documentation for any purpose, provided that the above copyright notice and
* the following two paragraphs appear in all copies of this software.
*
* REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
* HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
* MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
* SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
* REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.wise.portal.dao.chatbot;

import java.util.List;

import org.wise.portal.dao.SimpleDao;
import org.wise.portal.domain.run.Run;
import org.wise.portal.domain.workgroup.Workgroup;
import org.wise.vle.domain.chatbot.Chat;

/**
* Data Access Object interface for Chat
*
* @author Hiroki Terashima
*/
public interface ChatDao<T extends Chat> extends SimpleDao<T> {

/**
* Get all chats for a specific run and workgroup
*
* @param run the run
* @param workgroup the workgroup
* @return list of chats
*/
List<Chat> getChatsByRunAndWorkgroup(Run run, Workgroup workgroup);
}
37 changes: 37 additions & 0 deletions src/main/java/org/wise/portal/dao/chatbot/ChatMessageDao.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Copyright (c) 2007-2025 Regents of the University of California (Regents).
* Created by WISE, Graduate School of Education, University of California, Berkeley.
*
* This software is distributed under the GNU General Public License, v3,
* or (at your option) any later version.
*
* Permission is hereby granted, without written agreement and without license
* or royalty fees, to use, copy, modify, and distribute this software and its
* documentation for any purpose, provided that the above copyright notice and
* the following two paragraphs appear in all copies of this software.
*
* REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
* HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
* MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
* SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
* REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.wise.portal.dao.chatbot;

import org.wise.portal.dao.SimpleDao;
import org.wise.vle.domain.chatbot.ChatMessage;

/**
* Data Access Object interface for ChatMessage
*
* @author Hiroki Terashima
*/
public interface ChatMessageDao<T extends ChatMessage> extends SimpleDao<T> {
// ChatMessage operations are primarily handled through the Chat entity
// This interface extends SimpleDao for basic CRUD operations
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
* Copyright (c) 2007-2025 Regents of the University of California (Regents).
* Created by WISE, Graduate School of Education, University of California, Berkeley.
*
* This software is distributed under the GNU General Public License, v3,
* or (at your option) any later version.
*
* Permission is hereby granted, without written agreement and without license
* or royalty fees, to use, copy, modify, and distribute this software and its
* documentation for any purpose, provided that the above copyright notice and
* the following two paragraphs appear in all copies of this software.
*
* REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
* HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
* MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
* SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
* REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.wise.portal.dao.chatbot.impl;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.springframework.stereotype.Repository;
import org.wise.portal.dao.chatbot.ChatDao;
import org.wise.portal.dao.impl.AbstractHibernateDao;
import org.wise.portal.domain.run.Run;
import org.wise.portal.domain.workgroup.Workgroup;
import org.wise.vle.domain.chatbot.Chat;

/**
* Hibernate implementation of ChatDao
*
* @author Hiroki Terashima
*/
@Repository
public class HibernateChatDao extends AbstractHibernateDao<Chat> implements ChatDao<Chat> {

@Override
protected Class<? extends Chat> getDataObjectClass() {
return Chat.class;
}

@Override
@SuppressWarnings("unchecked")
public List<Chat> getChatsByRunAndWorkgroup(Run run, Workgroup workgroup) {
CriteriaBuilder cb = getCriteriaBuilder();
CriteriaQuery<Chat> cq = cb.createQuery(Chat.class);
Root<Chat> chatRoot = cq.from(Chat.class);
List<Predicate> predicates = new ArrayList<>();

if (run != null) {
predicates.add(cb.equal(chatRoot.get("run"), run));
}
if (workgroup != null) {
predicates.add(cb.equal(chatRoot.get("workgroup"), workgroup));
}

cq.select(chatRoot).where(predicates.toArray(new Predicate[predicates.size()]))
.orderBy(cb.desc(chatRoot.get("lastUpdated")));

TypedQuery<Chat> query = entityManager.createQuery(cq);
return (List<Chat>) (Object) query.getResultList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Copyright (c) 2007-2025 Regents of the University of California (Regents).
* Created by WISE, Graduate School of Education, University of California, Berkeley.
*
* This software is distributed under the GNU General Public License, v3,
* or (at your option) any later version.
*
* Permission is hereby granted, without written agreement and without license
* or royalty fees, to use, copy, modify, and distribute this software and its
* documentation for any purpose, provided that the above copyright notice and
* the following two paragraphs appear in all copies of this software.
*
* REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
* HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
* MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
* SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
* REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.wise.portal.dao.chatbot.impl;

import org.springframework.stereotype.Repository;
import org.wise.portal.dao.chatbot.ChatMessageDao;
import org.wise.portal.dao.impl.AbstractHibernateDao;
import org.wise.vle.domain.chatbot.ChatMessage;

/**
* Hibernate implementation of ChatMessageDao
*
* @author Hiroki Terashima
*/
@Repository
public class HibernateChatMessageDao extends AbstractHibernateDao<ChatMessage>
implements ChatMessageDao<ChatMessage> {

@Override
protected Class<? extends ChatMessage> getDataObjectClass() {
return ChatMessage.class;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.wise.portal.presentation.web;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/aws-bedrock/chat")
public class AWSBedrockController {

@Autowired
Environment appProperties;

@ResponseBody
@Secured("ROLE_USER")
@PostMapping
protected String sendChatMessage(@RequestBody String body) {
String apiKey = appProperties.getProperty("aws.bedrock.api.key");
if (apiKey == null || apiKey.isEmpty()) {
throw new RuntimeException("aws.bedrock.api.key is not set");
}
String apiEndpoint = appProperties.getProperty("aws.bedrock.runtime.endpoint");
if (apiEndpoint == null || apiEndpoint.isEmpty()) {
throw new RuntimeException("aws.bedrock.runtime.endpoint is not set");
}
// assume openai-only support for now. We'll add other models later.
apiEndpoint += "/openai/v1/chat/completions";

try {
URL url = new URL(apiEndpoint);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Authorization", "Bearer " + apiKey);
connection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
connection.setRequestProperty("Accept-Charset", "UTF-8");
connection.setDoOutput(true);
OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
writer.write(body);
writer.flush();
writer.close();
BufferedReader br = new BufferedReader(
new InputStreamReader(connection.getInputStream(), "UTF-8"));
String line;
StringBuffer response = new StringBuffer();
while ((line = br.readLine()) != null) {
response.append(line);
}
br.close();
return response.toString();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package org.wise.portal.presentation.web.controllers;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.wise.portal.dao.ObjectNotFoundException;
import org.wise.portal.domain.run.impl.RunImpl;
import org.wise.portal.domain.workgroup.impl.WorkgroupImpl;
import org.wise.portal.service.chatbot.ChatbotService;
import org.wise.vle.domain.chatbot.Chat;

/**
* REST controller for managing chatbot conversations
*
* @author Hiroki Terashima
*/
@RestController
@RequestMapping("/api/chatbot")
@Secured("ROLE_USER")
public class ChatbotController {

@Autowired
private ChatbotService chatbotService;

/**
* Get all chats for a specific run and workgroup
*
* @param run the run ID
* @param workgroup the workgroup ID
* @return list of all chats
*/
@GetMapping("/chats/{run}/{workgroup}")
public ResponseEntity<List<Chat>> getAllChats(@PathVariable RunImpl run,
@PathVariable WorkgroupImpl workgroup) {
return ResponseEntity.ok(chatbotService.getAllChats(run, workgroup));
}

/**
* Create a new chat
*
* @param run the run ID
* @param workgroup the workgroup ID
* @param chat the chat data
* @return the created chat
*/
@PostMapping("/chats/{run}/{workgroup}")
public ResponseEntity<Chat> createChat(@PathVariable RunImpl run,
@PathVariable WorkgroupImpl workgroup, @RequestBody Chat chat) {
return ResponseEntity.status(HttpStatus.CREATED)
.body(chatbotService.createChat(run, workgroup, chat));
}

/**
* Update an existing chat
*
* @param run the run ID
* @param workgroup the workgroup ID
* @param chatId the chat ID
* @param chat the updated chat data
* @return the updated chat
* @throws ObjectNotFoundException when the chat is not found
*/
@PutMapping("/chats/{run}/{workgroup}/{chatId}")
public ResponseEntity<Chat> updateChat(@PathVariable RunImpl run,
@PathVariable WorkgroupImpl workgroup, @PathVariable Long chatId, @RequestBody Chat chat)
throws ObjectNotFoundException {
return ResponseEntity.ok(chatbotService.updateChat(run, workgroup, chatId, chat));
}

/**
* Delete a chat
*
* @param run the run ID
* @param workgroup the workgroup ID
* @param chatId the chat ID
* @return success response
* @throws ObjectNotFoundException when the chat is not found
*/
@DeleteMapping("/chats/{run}/{workgroup}/{chatId}")
public ResponseEntity<Void> deleteChat(@PathVariable RunImpl run,
@PathVariable WorkgroupImpl workgroup, @PathVariable Long chatId) throws ObjectNotFoundException {
chatbotService.deleteChat(run, workgroup, chatId);
return ResponseEntity.noContent().build();
}
}
Loading