Skip to content

Commit 36909d0

Browse files
feat(Chatbot): New endpoint for chatbot (#313)
1 parent 774c632 commit 36909d0

File tree

13 files changed

+758
-0
lines changed

13 files changed

+758
-0
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* Copyright (c) 2007-2025 Regents of the University of California (Regents).
3+
* Created by WISE, Graduate School of Education, University of California, Berkeley.
4+
*
5+
* This software is distributed under the GNU General Public License, v3,
6+
* or (at your option) any later version.
7+
*
8+
* Permission is hereby granted, without written agreement and without license
9+
* or royalty fees, to use, copy, modify, and distribute this software and its
10+
* documentation for any purpose, provided that the above copyright notice and
11+
* the following two paragraphs appear in all copies of this software.
12+
*
13+
* REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
14+
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
15+
* PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
16+
* HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
17+
* MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
18+
*
19+
* IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
20+
* SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
21+
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
22+
* REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23+
*/
24+
package org.wise.portal.dao.chatbot;
25+
26+
import java.util.List;
27+
28+
import org.wise.portal.dao.SimpleDao;
29+
import org.wise.portal.domain.run.Run;
30+
import org.wise.portal.domain.workgroup.Workgroup;
31+
import org.wise.vle.domain.chatbot.Chat;
32+
33+
/**
34+
* Data Access Object interface for Chat
35+
*
36+
* @author Hiroki Terashima
37+
*/
38+
public interface ChatDao<T extends Chat> extends SimpleDao<T> {
39+
40+
/**
41+
* Get all chats for a specific run and workgroup
42+
*
43+
* @param run the run
44+
* @param workgroup the workgroup
45+
* @return list of chats
46+
*/
47+
List<Chat> getChatsByRunAndWorkgroup(Run run, Workgroup workgroup);
48+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* Copyright (c) 2007-2025 Regents of the University of California (Regents).
3+
* Created by WISE, Graduate School of Education, University of California, Berkeley.
4+
*
5+
* This software is distributed under the GNU General Public License, v3,
6+
* or (at your option) any later version.
7+
*
8+
* Permission is hereby granted, without written agreement and without license
9+
* or royalty fees, to use, copy, modify, and distribute this software and its
10+
* documentation for any purpose, provided that the above copyright notice and
11+
* the following two paragraphs appear in all copies of this software.
12+
*
13+
* REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
14+
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
15+
* PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
16+
* HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
17+
* MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
18+
*
19+
* IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
20+
* SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
21+
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
22+
* REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23+
*/
24+
package org.wise.portal.dao.chatbot;
25+
26+
import org.wise.portal.dao.SimpleDao;
27+
import org.wise.vle.domain.chatbot.ChatMessage;
28+
29+
/**
30+
* Data Access Object interface for ChatMessage
31+
*
32+
* @author Hiroki Terashima
33+
*/
34+
public interface ChatMessageDao<T extends ChatMessage> extends SimpleDao<T> {
35+
// ChatMessage operations are primarily handled through the Chat entity
36+
// This interface extends SimpleDao for basic CRUD operations
37+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* Copyright (c) 2007-2025 Regents of the University of California (Regents).
3+
* Created by WISE, Graduate School of Education, University of California, Berkeley.
4+
*
5+
* This software is distributed under the GNU General Public License, v3,
6+
* or (at your option) any later version.
7+
*
8+
* Permission is hereby granted, without written agreement and without license
9+
* or royalty fees, to use, copy, modify, and distribute this software and its
10+
* documentation for any purpose, provided that the above copyright notice and
11+
* the following two paragraphs appear in all copies of this software.
12+
*
13+
* REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
14+
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
15+
* PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
16+
* HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
17+
* MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
18+
*
19+
* IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
20+
* SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
21+
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
22+
* REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23+
*/
24+
package org.wise.portal.dao.chatbot.impl;
25+
26+
import java.util.ArrayList;
27+
import java.util.List;
28+
29+
import javax.persistence.TypedQuery;
30+
import javax.persistence.criteria.CriteriaBuilder;
31+
import javax.persistence.criteria.CriteriaQuery;
32+
import javax.persistence.criteria.Predicate;
33+
import javax.persistence.criteria.Root;
34+
35+
import org.springframework.stereotype.Repository;
36+
import org.wise.portal.dao.chatbot.ChatDao;
37+
import org.wise.portal.dao.impl.AbstractHibernateDao;
38+
import org.wise.portal.domain.run.Run;
39+
import org.wise.portal.domain.workgroup.Workgroup;
40+
import org.wise.vle.domain.chatbot.Chat;
41+
42+
/**
43+
* Hibernate implementation of ChatDao
44+
*
45+
* @author Hiroki Terashima
46+
*/
47+
@Repository
48+
public class HibernateChatDao extends AbstractHibernateDao<Chat> implements ChatDao<Chat> {
49+
50+
@Override
51+
protected Class<? extends Chat> getDataObjectClass() {
52+
return Chat.class;
53+
}
54+
55+
@Override
56+
@SuppressWarnings("unchecked")
57+
public List<Chat> getChatsByRunAndWorkgroup(Run run, Workgroup workgroup) {
58+
CriteriaBuilder cb = getCriteriaBuilder();
59+
CriteriaQuery<Chat> cq = cb.createQuery(Chat.class);
60+
Root<Chat> chatRoot = cq.from(Chat.class);
61+
List<Predicate> predicates = new ArrayList<>();
62+
63+
if (run != null) {
64+
predicates.add(cb.equal(chatRoot.get("run"), run));
65+
}
66+
if (workgroup != null) {
67+
predicates.add(cb.equal(chatRoot.get("workgroup"), workgroup));
68+
}
69+
70+
cq.select(chatRoot).where(predicates.toArray(new Predicate[predicates.size()]))
71+
.orderBy(cb.desc(chatRoot.get("lastUpdated")));
72+
73+
TypedQuery<Chat> query = entityManager.createQuery(cq);
74+
return (List<Chat>) (Object) query.getResultList();
75+
}
76+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* Copyright (c) 2007-2025 Regents of the University of California (Regents).
3+
* Created by WISE, Graduate School of Education, University of California, Berkeley.
4+
*
5+
* This software is distributed under the GNU General Public License, v3,
6+
* or (at your option) any later version.
7+
*
8+
* Permission is hereby granted, without written agreement and without license
9+
* or royalty fees, to use, copy, modify, and distribute this software and its
10+
* documentation for any purpose, provided that the above copyright notice and
11+
* the following two paragraphs appear in all copies of this software.
12+
*
13+
* REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
14+
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
15+
* PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
16+
* HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
17+
* MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
18+
*
19+
* IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
20+
* SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
21+
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
22+
* REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23+
*/
24+
package org.wise.portal.dao.chatbot.impl;
25+
26+
import org.springframework.stereotype.Repository;
27+
import org.wise.portal.dao.chatbot.ChatMessageDao;
28+
import org.wise.portal.dao.impl.AbstractHibernateDao;
29+
import org.wise.vle.domain.chatbot.ChatMessage;
30+
31+
/**
32+
* Hibernate implementation of ChatMessageDao
33+
*
34+
* @author Hiroki Terashima
35+
*/
36+
@Repository
37+
public class HibernateChatMessageDao extends AbstractHibernateDao<ChatMessage>
38+
implements ChatMessageDao<ChatMessage> {
39+
40+
@Override
41+
protected Class<? extends ChatMessage> getDataObjectClass() {
42+
return ChatMessage.class;
43+
}
44+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package org.wise.portal.presentation.web;
2+
3+
import java.io.BufferedReader;
4+
import java.io.IOException;
5+
import java.io.InputStreamReader;
6+
import java.io.OutputStreamWriter;
7+
import java.net.HttpURLConnection;
8+
import java.net.URL;
9+
10+
import org.springframework.beans.factory.annotation.Autowired;
11+
import org.springframework.core.env.Environment;
12+
import org.springframework.security.access.annotation.Secured;
13+
import org.springframework.web.bind.annotation.PostMapping;
14+
import org.springframework.web.bind.annotation.RequestBody;
15+
import org.springframework.web.bind.annotation.RequestMapping;
16+
import org.springframework.web.bind.annotation.ResponseBody;
17+
import org.springframework.web.bind.annotation.RestController;
18+
19+
@RestController
20+
@RequestMapping("/api/aws-bedrock/chat")
21+
public class AWSBedrockController {
22+
23+
@Autowired
24+
Environment appProperties;
25+
26+
@ResponseBody
27+
@Secured("ROLE_USER")
28+
@PostMapping
29+
protected String sendChatMessage(@RequestBody String body) {
30+
String apiKey = appProperties.getProperty("aws.bedrock.api.key");
31+
if (apiKey == null || apiKey.isEmpty()) {
32+
throw new RuntimeException("aws.bedrock.api.key is not set");
33+
}
34+
String apiEndpoint = appProperties.getProperty("aws.bedrock.runtime.endpoint");
35+
if (apiEndpoint == null || apiEndpoint.isEmpty()) {
36+
throw new RuntimeException("aws.bedrock.runtime.endpoint is not set");
37+
}
38+
// assume openai-only support for now. We'll add other models later.
39+
apiEndpoint += "/openai/v1/chat/completions";
40+
41+
try {
42+
URL url = new URL(apiEndpoint);
43+
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
44+
connection.setRequestMethod("POST");
45+
connection.setRequestProperty("Authorization", "Bearer " + apiKey);
46+
connection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
47+
connection.setRequestProperty("Accept-Charset", "UTF-8");
48+
connection.setDoOutput(true);
49+
OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
50+
writer.write(body);
51+
writer.flush();
52+
writer.close();
53+
BufferedReader br = new BufferedReader(
54+
new InputStreamReader(connection.getInputStream(), "UTF-8"));
55+
String line;
56+
StringBuffer response = new StringBuffer();
57+
while ((line = br.readLine()) != null) {
58+
response.append(line);
59+
}
60+
br.close();
61+
return response.toString();
62+
} catch (IOException e) {
63+
throw new RuntimeException(e);
64+
}
65+
}
66+
67+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package org.wise.portal.presentation.web.controllers;
2+
3+
import java.util.List;
4+
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.http.HttpStatus;
7+
import org.springframework.http.ResponseEntity;
8+
import org.springframework.security.access.annotation.Secured;
9+
import org.springframework.web.bind.annotation.DeleteMapping;
10+
import org.springframework.web.bind.annotation.GetMapping;
11+
import org.springframework.web.bind.annotation.PathVariable;
12+
import org.springframework.web.bind.annotation.PostMapping;
13+
import org.springframework.web.bind.annotation.PutMapping;
14+
import org.springframework.web.bind.annotation.RequestBody;
15+
import org.springframework.web.bind.annotation.RequestMapping;
16+
import org.springframework.web.bind.annotation.RestController;
17+
import org.wise.portal.dao.ObjectNotFoundException;
18+
import org.wise.portal.domain.run.impl.RunImpl;
19+
import org.wise.portal.domain.workgroup.impl.WorkgroupImpl;
20+
import org.wise.portal.service.chatbot.ChatbotService;
21+
import org.wise.vle.domain.chatbot.Chat;
22+
23+
/**
24+
* REST controller for managing chatbot conversations
25+
*
26+
* @author Hiroki Terashima
27+
*/
28+
@RestController
29+
@RequestMapping("/api/chatbot")
30+
@Secured("ROLE_USER")
31+
public class ChatbotController {
32+
33+
@Autowired
34+
private ChatbotService chatbotService;
35+
36+
/**
37+
* Get all chats for a specific run and workgroup
38+
*
39+
* @param run the run ID
40+
* @param workgroup the workgroup ID
41+
* @return list of all chats
42+
*/
43+
@GetMapping("/chats/{run}/{workgroup}")
44+
public ResponseEntity<List<Chat>> getAllChats(@PathVariable RunImpl run,
45+
@PathVariable WorkgroupImpl workgroup) {
46+
return ResponseEntity.ok(chatbotService.getAllChats(run, workgroup));
47+
}
48+
49+
/**
50+
* Create a new chat
51+
*
52+
* @param run the run ID
53+
* @param workgroup the workgroup ID
54+
* @param chat the chat data
55+
* @return the created chat
56+
*/
57+
@PostMapping("/chats/{run}/{workgroup}")
58+
public ResponseEntity<Chat> createChat(@PathVariable RunImpl run,
59+
@PathVariable WorkgroupImpl workgroup, @RequestBody Chat chat) {
60+
return ResponseEntity.status(HttpStatus.CREATED)
61+
.body(chatbotService.createChat(run, workgroup, chat));
62+
}
63+
64+
/**
65+
* Update an existing chat
66+
*
67+
* @param run the run ID
68+
* @param workgroup the workgroup ID
69+
* @param chatId the chat ID
70+
* @param chat the updated chat data
71+
* @return the updated chat
72+
* @throws ObjectNotFoundException when the chat is not found
73+
*/
74+
@PutMapping("/chats/{run}/{workgroup}/{chatId}")
75+
public ResponseEntity<Chat> updateChat(@PathVariable RunImpl run,
76+
@PathVariable WorkgroupImpl workgroup, @PathVariable Long chatId, @RequestBody Chat chat)
77+
throws ObjectNotFoundException {
78+
return ResponseEntity.ok(chatbotService.updateChat(run, workgroup, chatId, chat));
79+
}
80+
81+
/**
82+
* Delete a chat
83+
*
84+
* @param run the run ID
85+
* @param workgroup the workgroup ID
86+
* @param chatId the chat ID
87+
* @return success response
88+
* @throws ObjectNotFoundException when the chat is not found
89+
*/
90+
@DeleteMapping("/chats/{run}/{workgroup}/{chatId}")
91+
public ResponseEntity<Void> deleteChat(@PathVariable RunImpl run,
92+
@PathVariable WorkgroupImpl workgroup, @PathVariable Long chatId) throws ObjectNotFoundException {
93+
chatbotService.deleteChat(run, workgroup, chatId);
94+
return ResponseEntity.noContent().build();
95+
}
96+
}

0 commit comments

Comments
 (0)