[Core] Refactor chat (#392)

This commit is contained in:
qianmoQ 2023-07-10 20:11:28 +08:00 committed by GitHub
commit 711cf3e3c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 1529 additions and 524 deletions

View File

@ -18,18 +18,18 @@ spring.resources.add-mappings=false
spring.web.resources.add-mappings=true
################################ Database configure #################################
### The system uses h2 storage by default, and the following related configurations can be modified
#spring.datasource.driverClassName=org.h2.Driver
#spring.datasource.url=jdbc:h2:mem:datacap
#spring.datasource.username=h2
#spring.datasource.password=h2
#spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
#spring.h2.console.enabled=true
## The system uses h2 storage by default, and the following related configurations can be modified
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:datacap
spring.datasource.username=h2
spring.datasource.password=h2
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
### If you enable MySQL storage, please modify the following configuration
spring.datasource.url=jdbc:mysql://localhost:3306/datacap?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false&useOldAliasMetadataBehavior=true&jdbcCompliantTruncation=false
spring.datasource.username=root
spring.datasource.password=12345678
#spring.datasource.url=jdbc:mysql://localhost:3306/datacap?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false&useOldAliasMetadataBehavior=true&jdbcCompliantTruncation=false
#spring.datasource.username=root
#spring.datasource.password=12345678
################################ Redis configure #################################
### Set redis environment

View File

@ -0,0 +1,51 @@
package io.edurt.datacap.server.controller;
import io.edurt.datacap.common.response.CommonResponse;
import io.edurt.datacap.service.body.FilterBody;
import io.edurt.datacap.service.service.BaseService;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.web.bind.annotation.DeleteMapping;
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.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import java.io.Serializable;
public abstract class BaseController<T>
implements Serializable
{
private final PagingAndSortingRepository repository;
private final BaseService<T> service;
protected BaseController(PagingAndSortingRepository repository, BaseService<T> service)
{
this.repository = repository;
this.service = service;
}
/**
* Get data based on pagination
*/
@PostMapping(value = "list")
public CommonResponse list(@RequestBody FilterBody filter)
{
return service.getAll(repository, filter);
}
/**
* Save changes
*/
@RequestMapping(method = {RequestMethod.POST, RequestMethod.PUT})
public CommonResponse saveAndUpdate(@RequestBody T configure)
{
return service.saveOrUpdate(repository, configure);
}
@DeleteMapping
public CommonResponse delete(@RequestParam(value = "id") Long id)
{
return service.deleteById(repository, id);
}
}

View File

@ -0,0 +1,46 @@
package io.edurt.datacap.server.controller;
import io.edurt.datacap.common.response.CommonResponse;
import io.edurt.datacap.service.body.FilterBody;
import io.edurt.datacap.service.entity.ChatEntity;
import io.edurt.datacap.service.entity.MessageEntity;
import io.edurt.datacap.service.repository.ChatRepository;
import io.edurt.datacap.service.service.ChatService;
import io.edurt.datacap.service.service.MessageService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController()
@RequestMapping(value = "/api/v1/chat")
public class ChatController
extends BaseController<ChatEntity>
{
private final ChatRepository repository;
private final ChatService service;
private final MessageService messageService;
public ChatController(ChatRepository repository, ChatService service, MessageService messageService)
{
super(repository, service);
this.repository = repository;
this.service = service;
this.messageService = messageService;
}
@Override
public CommonResponse list(@RequestBody FilterBody filter)
{
return this.service.getAllByUser(filter);
}
@GetMapping(value = "{id}/messages")
public CommonResponse<List<MessageEntity>> getMessagesByChat(@PathVariable(value = "id") Long id)
{
return this.messageService.getMessageByChatAndUser(id);
}
}

View File

@ -0,0 +1,33 @@
package io.edurt.datacap.server.controller;
import io.edurt.datacap.common.response.CommonResponse;
import io.edurt.datacap.service.body.UserQuestionBody;
import io.edurt.datacap.service.entity.MessageEntity;
import io.edurt.datacap.service.repository.MessageRepository;
import io.edurt.datacap.service.service.MessageService;
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.RestController;
@RestController()
@RequestMapping(value = "/api/v1/message")
public class MessageController
extends BaseController<MessageEntity>
{
private final MessageRepository repository;
private final MessageService service;
public MessageController(MessageRepository repository, MessageService service)
{
super(repository, service);
this.repository = repository;
this.service = service;
}
@PostMapping(value = "ai/reply")
public CommonResponse<MessageEntity> aiReply(@RequestBody UserQuestionBody configure)
{
return this.service.aiReply(configure);
}
}

View File

@ -5,7 +5,6 @@ import io.edurt.datacap.common.response.CommonResponse;
import io.edurt.datacap.service.body.FilterBody;
import io.edurt.datacap.service.body.UserNameBody;
import io.edurt.datacap.service.body.UserPasswordBody;
import io.edurt.datacap.service.body.UserQuestionBody;
import io.edurt.datacap.service.body.user.UserRole;
import io.edurt.datacap.service.entity.PageEntity;
import io.edurt.datacap.service.entity.RoleEntity;
@ -74,12 +73,6 @@ public class UserController
return this.userLogService.getAllByFilter(filter);
}
@PostMapping(value = "startChat")
public CommonResponse<Object> startChat(@RequestBody UserQuestionBody configure)
{
return this.userService.startChat(configure);
}
@GetMapping(value = "sugs/{id}")
public CommonResponse<List<Object>> getSugs(@PathVariable Long id)
{

View File

@ -488,3 +488,5 @@ values ('2', '7'),
-- --------------------------------
alter table `menus`
add column `redirect` bigint default 0;
ALTER TABLE `menus`
ADD COLUMN `is_new` BOOLEAN DEFAULT FALSE;

View File

@ -217,22 +217,6 @@ OFFSET ${page:Integer}', 'Get all data from table by limited', 'MySQL,ClickHouse
, '[{"column":"table","type":"String","expression":"${table:String}"},{"column":"size","type":"Integer","expression":"${size:Integer}"},{"column":"page","type":"Integer","expression":"${page:Integer}"}]'
, '2023-01-10 13:31:10', '2023-01-10 13:31:10', 0);
-- --------------------------------
-- Table structure for user_chat
-- --------------------------------
CREATE TABLE IF NOT EXISTS user_chat
(
id int PRIMARY KEY AUTO_INCREMENT,
name varchar(255) NOT NULL,
question text NOT NULL,
answer text NULL,
type varchar(100) NULL,
create_time date NULL DEFAULT CURRENT_TIMESTAMP,
end_time date NULL ON UPDATE CURRENT_TIMESTAMP,
elapsed bigint NULL,
user_id int NOT NULL,
is_new boolean NULL DEFAULT 1
);
-- --------------------------------
-- Table structure for user_log
-- --------------------------------
CREATE TABLE IF NOT EXISTS user_log
@ -263,22 +247,25 @@ VALUES (2, 2);
-- --------------------------------
-- Table structure for users
-- --------------------------------
CREATE TABLE IF NOT EXISTS users
CREATE TABLE IF NOT EXISTS datacap_user
(
id bigint PRIMARY KEY AUTO_INCREMENT,
username varchar(255) NULL COMMENT ' ',
password varchar(255) NULL COMMENT ' ',
create_time date NULL DEFAULT CURRENT_TIMESTAMP(5),
third_configure text NULL
chat_configure text NULL
);
TRUNCATE TABLE users;
ALTER TABLE users
TRUNCATE TABLE datacap_user;
ALTER TABLE datacap_user
ALTER COLUMN id RESTART WITH 1;
INSERT INTO users (username, password)
ALTER TABLE datacap_user
ADD COLUMN `is_system` BOOLEAN DEFAULT FALSE;
INSERT INTO datacap_user (username, password)
VALUES ('admin', '$2a$10$ee2yg.Te14GpHppDUROAi.HzYR5Q.q2/5vrZvAr4TFY3J2iT663JG');
INSERT INTO users (username, password)
INSERT INTO datacap_user (username, password)
VALUES ('datacap', '$2a$10$bZ4XBRlYUjKfkBovWT9TuuXlEF7lpRxVrXS8iqyCjCHUqy4RPTL8.');
INSERT INTO datacap_user(username, is_system)
VALUES ('Ai', TRUE);
-- --------------------------------
-- Update to 1.11.0 --
-- --------------------------------
@ -305,3 +292,50 @@ WHERE
keyspace_name = ''${database:String}''
and table_name = ''${table:String}''', 'Get the data column from the database and table', 'Cassandra',
'[{"column":"database","type":"String","expression":"${database:String}"},{"column":"table","type":"String","expression":"${table:String}"}]', 1);
-- --------------------------------
-- Update to 1.12.0 --
-- --------------------------------
CREATE TABLE `datacap_chat`
(
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`active` BOOLEAN DEFAULT TRUE,
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`avatar` VARCHAR(255) DEFAULT NULL,
`description` VARCHAR(255) DEFAULT NULL
);
CREATE TABLE `datacap_chat_user_relation`
(
chat_id BIGINT,
user_id BIGINT
);
CREATE TABLE `datacap_message`
(
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`active` BOOLEAN DEFAULT TRUE,
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`content` TEXT DEFAULT NULL,
`model` VARCHAR(255) DEFAULT NULL,
`type` VARCHAR(100),
`prompt_tokens` BIGINT DEFAULT 0,
`completion_tokens` BIGINT DEFAULT 0,
`total_tokens` BIGINT DEFAULT 0
);
CREATE TABLE `datacap_message_user_relation`
(
message_id BIGINT,
user_id BIGINT
);
CREATE TABLE `datacap_message_chat_relation`
(
message_id BIGINT,
chat_id BIGINT
);

View File

@ -1,12 +1,58 @@
-- --------------------------------
-- Update to 1.12.0 --
-- --------------------------------
alter table `menus`
add column `redirect` bigint default 0,
add column `is_new` boolean default false;
ALTER TABLE `menus`
ADD COLUMN `redirect` BIGINT DEFAULT 0,
ADD COLUMN `is_new` BOOLEAN DEFAULT FALSE;
alter table `user_chat`
add column `model` varchar(30) default 'gpt-3.5-turbo-0613',
add column `prompt_tokens` bigint default 0,
add column `completion_tokens` bigint default 0,
add column `total_tokens` bigint default 0;
RENAME TABLE `users` TO `datacap_user`;
ALTER TABLE `datacap_user`
ADD COLUMN `is_system` BOOLEAN DEFAULT FALSE;
INSERT INTO `datacap_user`(`username`, `is_system`)
VALUES ('Ai', TRUE);
CREATE TABLE `datacap_chat`
(
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`active` BOOLEAN DEFAULT TRUE,
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`avatar` VARCHAR(255) DEFAULT NULL,
`description` VARCHAR(255) DEFAULT NULL
);
CREATE TABLE `datacap_chat_user_relation`
(
chat_id BIGINT,
user_id BIGINT
);
CREATE TABLE `datacap_message`
(
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`active` BOOLEAN DEFAULT TRUE,
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`content` TEXT DEFAULT NULL,
`model` VARCHAR(255) DEFAULT NULL,
`type` VARCHAR(100),
`prompt_tokens` BIGINT DEFAULT 0,
`completion_tokens` BIGINT DEFAULT 0,
`total_tokens` BIGINT DEFAULT 0
);
CREATE TABLE `datacap_message_user_relation`
(
message_id BIGINT,
user_id BIGINT
);
CREATE TABLE `datacap_message_chat_relation`
(
message_id BIGINT,
chat_id BIGINT
);

View File

@ -794,13 +794,61 @@ WHERE
and table_name = ''${table:String}''', 'Get the data column from the database and table', 'Cassandra',
'[{"column":"database","type":"String","expression":"${database:String}"},{"column":"table","type":"String","expression":"${table:String}"}]', 1);
alter table `menus`
add column `redirect` bigint default 0,
add column `is_new` boolean default false;
-- --------------------------------
-- Update to 1.12.0 --
-- --------------------------------
ALTER TABLE `menus`
ADD COLUMN `redirect` BIGINT DEFAULT 0,
ADD COLUMN `is_new` BOOLEAN DEFAULT FALSE;
alter table `user_chat`
add column `model` varchar(30) default 'gpt-3.5-turbo-0613',
add column `prompt_tokens` bigint default 0,
add column `completion_tokens` bigint default 0,
add column `total_tokens` bigint default 0;
RENAME TABLE `users` TO `datacap_user`;
ALTER TABLE `datacap_user`
ADD COLUMN `is_system` BOOLEAN DEFAULT FALSE;
INSERT INTO `datacap_user`(`username`, `is_system`)
VALUES ('Ai', TRUE);
CREATE TABLE `datacap_chat`
(
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`active` BOOLEAN DEFAULT TRUE,
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`avatar` VARCHAR(255) DEFAULT NULL,
`description` VARCHAR(255) DEFAULT NULL
);
CREATE TABLE `datacap_chat_user_relation`
(
chat_id BIGINT,
user_id BIGINT
);
CREATE TABLE `datacap_message`
(
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`active` BOOLEAN DEFAULT TRUE,
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`content` TEXT DEFAULT NULL,
`model` VARCHAR(255) DEFAULT NULL,
`type` VARCHAR(100),
`prompt_tokens` BIGINT DEFAULT 0,
`completion_tokens` BIGINT DEFAULT 0,
`total_tokens` BIGINT DEFAULT 0
);
CREATE TABLE `datacap_message_user_relation`
(
message_id BIGINT,
user_id BIGINT
);
CREATE TABLE `datacap_message_chat_relation`
(
message_id BIGINT,
chat_id BIGINT
);

View File

@ -0,0 +1,21 @@
package io.edurt.datacap.service.component.chat;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Builder;
import lombok.Data;
import lombok.ToString;
@Data
@ToString
@Builder
public class UserComponent
{
@JsonProperty(value = "_id")
private Long id;
@JsonProperty(value = "username")
private String username;
@JsonProperty(value = "avatar")
private String avatar;
}

View File

@ -0,0 +1,51 @@
package io.edurt.datacap.service.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import java.util.Date;
@Data
@SuperBuilder
@ToString
@NoArgsConstructor
@AllArgsConstructor
@MappedSuperclass
@SuppressFBWarnings(value = {"EI_EXPOSE_REP", "RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE"},
justification = "I prefer to suppress these FindBugs warnings")
public class BaseEntity
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "name")
private String name;
@Column(name = "active")
private Boolean active = true;
@Column(name = "create_time")
@CreatedDate
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
@Column(name = "update_time")
@LastModifiedDate
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
}

View File

@ -0,0 +1,46 @@
package io.edurt.datacap.service.entity;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Data
@SuperBuilder
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Entity
@EntityListeners(value = AuditingEntityListener.class)
@Table(name = "datacap_chat")
@SuppressFBWarnings(value = {"EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC"},
justification = "I prefer to suppress these FindBugs warnings")
public class ChatEntity
extends BaseEntity
{
@Column(name = "avatar")
private String avatar;
@Column(name = "description")
private String description;
@ManyToOne(fetch = FetchType.EAGER)
@JoinTable(name = "datacap_chat_user_relation",
joinColumns = @JoinColumn(name = "chat_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"))
@JsonIgnoreProperties(value = {"roles", "thirdConfigure"})
private UserEntity user;
}

View File

@ -0,0 +1,69 @@
package io.edurt.datacap.service.entity;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.edurt.datacap.service.enums.MessageType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Data
@SuperBuilder
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Entity
@EntityListeners(value = AuditingEntityListener.class)
@Table(name = "datacap_message")
@SuppressFBWarnings(value = {"EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC"},
justification = "I prefer to suppress these FindBugs warnings")
public class MessageEntity
extends BaseEntity
{
@Column(name = "content")
private String content;
@Column(name = "model")
private String model;
@Column(name = "type")
@Enumerated(EnumType.STRING)
private MessageType type;
@Column(name = "prompt_tokens")
private long promptTokens;
@Column(name = "completion_tokens")
private long completionTokens;
@Column(name = "total_tokens")
private long totalTokens;
@ManyToOne(fetch = FetchType.EAGER)
@JoinTable(name = "datacap_message_user_relation",
joinColumns = @JoinColumn(name = "message_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"))
@JsonIgnoreProperties(value = {"roles", "thirdConfigure"})
private UserEntity user;
@ManyToOne(fetch = FetchType.EAGER)
@JoinTable(name = "datacap_message_chat_relation",
joinColumns = @JoinColumn(name = "message_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "chat_id", referencedColumnName = "id"))
@JsonIgnoreProperties(value = {"user"})
private ChatEntity chat;
}

View File

@ -1,88 +0,0 @@
package io.edurt.datacap.service.entity;
import com.fasterxml.jackson.annotation.JsonIncludeProperties;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.edurt.datacap.service.validation.ValidationGroup;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.validation.constraints.NotBlank;
import java.sql.Timestamp;
@Entity
@Builder
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "user_chat")
@SuppressFBWarnings(value = {"EI_EXPOSE_REP"},
justification = "I prefer to suppress these FindBugs warnings")
public class UserChatEntity
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(groups = {
ValidationGroup.Crud.Create.class
})
@Column(name = "name")
private String name;
@Column(name = "question")
private String question;
@Column(name = "answer")
private String answer;
@Column(name = "type")
private String type;
@Column(name = "create_time", columnDefinition = "datetime(5) default CURRENT_TIMESTAMP()")
private Timestamp createTime;
@Column(name = "end_time", columnDefinition = "datetime(5) default CURRENT_TIMESTAMP()")
private Timestamp endTime;
@Column(name = "elapsed")
private Long elapsed;
@Column(name = "is_new")
private boolean isNew;
@Column(name = "model")
private String model;
@Column(name = "prompt_tokens")
private long promptTokens;
@Column(name = "completion_tokens")
private long completionTokens;
@Column(name = "total_tokens")
private long totalTokens;
@ManyToOne
@JoinColumn(name = "user_id")
@JsonIncludeProperties(value = {"id", "username"})
private UserEntity user;
public void setEndTime(Timestamp endTime)
{
this.endTime = endTime;
this.elapsed = this.endTime.getTime() - this.createTime.getTime();
}
}

View File

@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.edurt.datacap.service.validation.ValidationGroup;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@ -33,11 +34,12 @@ import java.util.List;
import java.util.Set;
@Entity
@Builder
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "users",
@Table(name = "datacap_user",
uniqueConstraints = {
@UniqueConstraint(columnNames = "username")
})
@ -68,8 +70,11 @@ public class UserEntity
@Column(name = "password")
private String password;
@Column(name = "third_configure")
private String thirdConfigure;
@Column(name = "chat_configure")
private String chatConfigure;
@Column(name = "is_system")
private boolean system;
@Column(name = "create_time", columnDefinition = "datetime(5) default CURRENT_TIMESTAMP()")
private Timestamp createTime;

View File

@ -0,0 +1,7 @@
package io.edurt.datacap.service.enums;
public enum MessageType
{
question,
answer
}

View File

@ -17,4 +17,6 @@ public class AiModel
private String host;
private String token;
private long timeout;
// Number of times a context needs to be associated
private int contentCount = 5;
}

View File

@ -0,0 +1,13 @@
package io.edurt.datacap.service.repository;
import io.edurt.datacap.service.entity.ChatEntity;
import io.edurt.datacap.service.entity.UserEntity;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.PagingAndSortingRepository;
public interface ChatRepository
extends PagingAndSortingRepository<ChatEntity, Long>
{
Page<ChatEntity> findAllByUser(UserEntity user, Pageable pageable);
}

View File

@ -0,0 +1,21 @@
package io.edurt.datacap.service.repository;
import io.edurt.datacap.service.entity.ChatEntity;
import io.edurt.datacap.service.entity.MessageEntity;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import java.util.List;
public interface MessageRepository
extends PagingAndSortingRepository<MessageEntity, Long>
{
List<MessageEntity> findAllByChatOrderByCreateTimeAsc(ChatEntity chat);
@Query(value = "SELECT m " +
"FROM MessageEntity AS m " +
"WHERE m.chat = ?1 AND m.id < ?2 " +
"ORDER BY m.createTime DESC")
List<MessageEntity> findTopByChatOrderByCreateTimeDesc(ChatEntity chat, long id, Pageable pageable);
}

View File

@ -1,13 +0,0 @@
package io.edurt.datacap.service.repository;
import io.edurt.datacap.service.entity.UserChatEntity;
import io.edurt.datacap.service.entity.UserEntity;
import org.springframework.data.repository.PagingAndSortingRepository;
import java.util.List;
public interface UserChatRepository
extends PagingAndSortingRepository<UserChatEntity, Long>
{
List<UserChatEntity> findTop5ByUserOrderByIdDesc(UserEntity user);
}

View File

@ -9,4 +9,6 @@ public interface UserRepository
extends PagingAndSortingRepository<UserEntity, Long>
{
Optional<UserEntity> findByUsername(String username);
UserEntity findByUsernameAndSystemTrue(String username);
}

View File

@ -25,5 +25,9 @@ public interface BaseService<T>
return CommonResponse.success(repository.save(configure));
}
CommonResponse<Long> delete(Long id);
default CommonResponse<Long> deleteById(PagingAndSortingRepository repository, Long id)
{
repository.deleteById(id);
return CommonResponse.success(id);
}
}

View File

@ -0,0 +1,12 @@
package io.edurt.datacap.service.service;
import io.edurt.datacap.common.response.CommonResponse;
import io.edurt.datacap.service.body.FilterBody;
import io.edurt.datacap.service.entity.ChatEntity;
import io.edurt.datacap.service.entity.PageEntity;
public interface ChatService
extends BaseService<ChatEntity>
{
CommonResponse<PageEntity<ChatEntity>> getAllByUser(FilterBody filter);
}

View File

@ -0,0 +1,15 @@
package io.edurt.datacap.service.service;
import io.edurt.datacap.common.response.CommonResponse;
import io.edurt.datacap.service.body.UserQuestionBody;
import io.edurt.datacap.service.entity.MessageEntity;
import java.util.List;
public interface MessageService
extends BaseService<MessageEntity>
{
CommonResponse<List<MessageEntity>> getMessageByChatAndUser(Long chatId);
CommonResponse<MessageEntity> aiReply(UserQuestionBody configure);
}

View File

@ -5,7 +5,6 @@ import io.edurt.datacap.common.response.JwtResponse;
import io.edurt.datacap.service.body.FilterBody;
import io.edurt.datacap.service.body.UserNameBody;
import io.edurt.datacap.service.body.UserPasswordBody;
import io.edurt.datacap.service.body.UserQuestionBody;
import io.edurt.datacap.service.entity.PageEntity;
import io.edurt.datacap.service.entity.UserEntity;
import io.edurt.datacap.service.model.AiModel;
@ -27,8 +26,6 @@ public interface UserService
CommonResponse<Long> changeThirdConfigure(AiModel configure);
CommonResponse<Object> startChat(UserQuestionBody configure);
CommonResponse<List<Object>> getSugs(Long id);
CommonResponse<List<TreeRecord>> getMenus();

View File

@ -0,0 +1,38 @@
package io.edurt.datacap.service.service.impl;
import io.edurt.datacap.common.response.CommonResponse;
import io.edurt.datacap.service.adapter.PageRequestAdapter;
import io.edurt.datacap.service.body.FilterBody;
import io.edurt.datacap.service.entity.ChatEntity;
import io.edurt.datacap.service.entity.PageEntity;
import io.edurt.datacap.service.repository.ChatRepository;
import io.edurt.datacap.service.security.UserDetailsService;
import io.edurt.datacap.service.service.ChatService;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Service;
@Service
public class ChatServiceImpl
implements ChatService
{
private final ChatRepository repository;
public ChatServiceImpl(ChatRepository repository)
{
this.repository = repository;
}
@Override
public CommonResponse<PageEntity<ChatEntity>> getAll(PagingAndSortingRepository repository, FilterBody filter)
{
return CommonResponse.success(PageEntity.build(repository.findAll(PageRequestAdapter.of(filter))));
}
@Override
public CommonResponse<PageEntity<ChatEntity>> getAllByUser(FilterBody filter)
{
Pageable pageable = PageRequestAdapter.of(filter);
return CommonResponse.success(PageEntity.build(repository.findAllByUser(UserDetailsService.getUser(), pageable)));
}
}

View File

@ -1,6 +1,5 @@
package io.edurt.datacap.service.service.impl;
import io.edurt.datacap.common.response.CommonResponse;
import io.edurt.datacap.service.repository.admin.MenuRepository;
import io.edurt.datacap.service.service.MenuService;
import org.springframework.stereotype.Service;
@ -15,10 +14,4 @@ public class MenuServiceImpl
{
this.menuRepository = menuRepository;
}
@Override
public CommonResponse<Long> delete(Long id)
{
return null;
}
}

View File

@ -0,0 +1,250 @@
package io.edurt.datacap.service.service.impl;
import com.google.common.collect.Lists;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.edurt.datacap.common.enums.ServiceState;
import io.edurt.datacap.common.response.CommonResponse;
import io.edurt.datacap.common.utils.AiSupportUtils;
import io.edurt.datacap.common.utils.JsonUtils;
import io.edurt.datacap.service.body.UserQuestionBody;
import io.edurt.datacap.service.entity.ChatEntity;
import io.edurt.datacap.service.entity.MessageEntity;
import io.edurt.datacap.service.entity.UserEntity;
import io.edurt.datacap.service.enums.MessageType;
import io.edurt.datacap.service.model.AiModel;
import io.edurt.datacap.service.repository.MessageRepository;
import io.edurt.datacap.service.repository.UserRepository;
import io.edurt.datacap.service.security.UserDetailsService;
import io.edurt.datacap.service.service.MessageService;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import org.apache.commons.lang.text.StrSubstitutor;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.devlive.sdk.openai.OpenAiClient;
import org.devlive.sdk.openai.entity.CompletionChatEntity;
import org.devlive.sdk.openai.entity.CompletionMessageEntity;
import org.devlive.sdk.openai.entity.UsageEntity;
import org.devlive.sdk.openai.model.CompletionMessageModel;
import org.devlive.sdk.openai.response.CompleteChatResponse;
import org.springframework.core.env.Environment;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
@SuppressFBWarnings(value = {"RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE"},
justification = "I prefer to suppress these FindBugs warnings")
public class MessageServiceImpl
implements MessageService
{
private final MessageRepository repository;
private final UserRepository userRepository;
private final Environment environment;
public MessageServiceImpl(MessageRepository repository, UserRepository userRepository, Environment environment)
{
this.repository = repository;
this.userRepository = userRepository;
this.environment = environment;
}
@Override
public CommonResponse<MessageEntity> saveOrUpdate(PagingAndSortingRepository repository, MessageEntity configure)
{
Optional<UserEntity> userOptional = this.userRepository.findById(configure.getUser().getId());
if (!userOptional.isPresent()) {
return CommonResponse.failure(ServiceState.USER_NOT_FOUND);
}
String openApiHost = environment.getProperty("datacap.openai.backend");
String openApiToken = environment.getProperty("datacap.openai.token");
String openApiModel = environment.getProperty("datacap.openai.model");
long openApiTimeout = Long.parseLong(environment.getProperty("datacap.openai.timeout"));
UserEntity user = userOptional.get();
MessageEntity questionMessage = MessageEntity.builder()
.user(user)
.chat(configure.getChat())
.model(StringUtils.isNotEmpty(configure.getModel()) ? configure.getModel() : openApiModel)
.content(configure.getContent())
.name(UUID.randomUUID().toString())
.type(MessageType.question)
.build();
this.repository.save(questionMessage);
int contentCount = 5;
boolean getContent = true;
if (StringUtils.isNotEmpty(configure.getModel())) {
openApiModel = configure.getModel();
}
// If user-defined configuration, use user configuration information
if (StringUtils.isNotEmpty(user.getChatConfigure())) {
AiModel aiModel = JsonUtils.toObject(user.getChatConfigure(), AiModel.class);
if (StringUtils.isNotEmpty(aiModel.getToken())) {
openApiHost = aiModel.getHost();
openApiToken = aiModel.getToken();
}
if (aiModel.getTimeout() > 0) {
openApiTimeout = aiModel.getTimeout();
}
if (aiModel.getContentCount() > 0) {
contentCount = aiModel.getContentCount();
}
}
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(openApiTimeout, TimeUnit.SECONDS)
.writeTimeout(openApiTimeout, TimeUnit.SECONDS)
.readTimeout(openApiTimeout, TimeUnit.SECONDS)
.build();
try (OpenAiClient openAiClient = OpenAiClient.builder()
.apiHost(openApiHost)
.apiKey(openApiToken)
.client(okHttpClient)
.build()) {
List<CompletionMessageEntity> messages = new ArrayList<>();
// Get the context in the conversation * 2
if (getContent) {
this.repository.findTopByChatOrderByCreateTimeDesc(configure.getChat(), questionMessage.getId(), Pageable.ofSize(contentCount * 2))
.stream()
.sorted(Comparator.comparing(MessageEntity::getId))
.forEach(message -> {
String role = message.getType() == MessageType.question ? CompletionMessageModel.USER.getName() : CompletionMessageModel.ASSISTANT.getName();
CompletionMessageEntity completionMessage = CompletionMessageEntity.builder()
.role(role)
.content(message.getContent())
.build();
messages.add(completionMessage);
});
}
CompletionMessageEntity message = CompletionMessageEntity.builder()
.role(CompletionMessageModel.USER.getName())
.content(questionMessage.getContent())
.build();
messages.add(message);
CompletionChatEntity chatCompletion = CompletionChatEntity.builder()
.messages(messages)
.maxTokens(2048)
.model(openApiModel)
.build();
CompleteChatResponse chatCompletionResponse = openAiClient.createChatCompletion(chatCompletion);
List<String> answer = Lists.newArrayList();
chatCompletionResponse.getChoices()
.forEach(e -> answer.add(e.getMessage().getContent()));
UsageEntity usage = chatCompletionResponse.getUsage();
UserEntity aiUser = this.userRepository.findByUsernameAndSystemTrue("Ai");
MessageEntity answerEntity = MessageEntity.builder()
.user(aiUser)
.chat(configure.getChat())
.model(openApiModel)
.content(String.join("\n", answer))
.type(MessageType.answer)
.promptTokens(usage.getPromptTokens())
.completionTokens(usage.getCompletionTokens())
.totalTokens(usage.getTotalTokens())
.name(UUID.randomUUID().toString())
.build();
this.repository.save(answerEntity);
return CommonResponse.success(answerEntity);
}
catch (Exception e) {
return CommonResponse.failure(e.getMessage());
}
}
@Override
public CommonResponse<List<MessageEntity>> getMessageByChatAndUser(Long chatId)
{
ChatEntity chat = ChatEntity.builder()
.id(chatId)
.build();
return CommonResponse.success(this.repository.findAllByChatOrderByCreateTimeAsc(chat));
}
@Override
public CommonResponse<MessageEntity> aiReply(UserQuestionBody configure)
{
String openApiHost = environment.getProperty("datacap.openai.backend");
String openApiToken = environment.getProperty("datacap.openai.token");
String openApiModel = environment.getProperty("datacap.openai.model");
long openApiTimeout = Long.parseLong(environment.getProperty("datacap.openai.timeout"));
UserEntity user = this.userRepository.findById(UserDetailsService.getUser().getId()).get();
if (StringUtils.isNotEmpty(configure.getModel())) {
openApiModel = configure.getModel();
}
// If user-defined configuration, use user configuration information
String forwardContent = null;
if (StringUtils.isNotEmpty(user.getChatConfigure())) {
AiModel aiModel = JsonUtils.toObject(user.getChatConfigure(), AiModel.class);
if (StringUtils.isNotEmpty(aiModel.getToken())) {
openApiHost = aiModel.getHost();
openApiToken = aiModel.getToken();
}
if (aiModel.getTimeout() > 0) {
openApiTimeout = aiModel.getTimeout();
}
}
try {
AiSupportUtils.AiSupportEnum type = AiSupportUtils.AiSupportEnum.valueOf(configure.getTransType());
String replaceContent = AiSupportUtils.getValue(configure.getLocale(), type);
Properties properties = new Properties();
properties.put("sql", configure.getContent());
if (ObjectUtils.isNotEmpty(configure.getEngine())) {
properties.put("engine", configure.getEngine());
}
if (ObjectUtils.isNotEmpty(configure.getError())) {
properties.put("error", configure.getError());
}
StrSubstitutor sub = new StrSubstitutor(properties);
forwardContent = sub.replace(replaceContent);
}
catch (Exception exception) {
log.warn("Ai type not set, ignore .");
}
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(openApiTimeout, TimeUnit.SECONDS)
.writeTimeout(openApiTimeout, TimeUnit.SECONDS)
.readTimeout(openApiTimeout, TimeUnit.SECONDS)
.build();
try (OpenAiClient openAiClient = OpenAiClient.builder()
.apiHost(openApiHost)
.apiKey(openApiToken)
.client(okHttpClient)
.build()) {
List<CompletionMessageEntity> messages = Lists.newArrayList(CompletionMessageEntity.builder()
.role(CompletionMessageModel.USER.getName())
.content(forwardContent)
.build());
CompletionChatEntity chatCompletion = CompletionChatEntity.builder()
.messages(messages)
.maxTokens(2048)
.model(openApiModel)
.build();
CompleteChatResponse chatCompletionResponse = openAiClient.createChatCompletion(chatCompletion);
List<String> answer = Lists.newArrayList();
chatCompletionResponse.getChoices()
.forEach(e -> answer.add(e.getMessage().getContent()));
UsageEntity usage = chatCompletionResponse.getUsage();
MessageEntity answerEntity = MessageEntity.builder()
.model(openApiModel)
.content(String.join("\n", answer))
.type(MessageType.answer)
.promptTokens(usage.getPromptTokens())
.completionTokens(usage.getCompletionTokens())
.totalTokens(usage.getTotalTokens())
.name(UUID.randomUUID().toString())
.build();
return CommonResponse.success(answerEntity);
}
catch (Exception e) {
return CommonResponse.failure(e.getMessage());
}
}
}

View File

@ -225,10 +225,4 @@ public class PipelineServiceImpl
}
properties.put(field.getField(), value);
}
@Override
public CommonResponse<Long> delete(Long id)
{
throw new UnsupportedOperationException("Not supported yet.");
}
}

View File

@ -34,12 +34,6 @@ public class RoleServiceImpl
this.menuRepository = menuRepository;
}
@Override
public CommonResponse<Long> delete(Long id)
{
return null;
}
@Override
public CommonResponse<Object> getMenusByRoleId(Long roleId)
{

View File

@ -1,6 +1,5 @@
package io.edurt.datacap.service.service.impl;
import io.edurt.datacap.common.response.CommonResponse;
import io.edurt.datacap.service.service.ScheduledTaskService;
import org.springframework.stereotype.Service;
@ -8,9 +7,4 @@ import org.springframework.stereotype.Service;
public class ScheduledTaskServiceImpl
implements ScheduledTaskService
{
@Override
public CommonResponse<Long> delete(Long id)
{
throw new UnsupportedOperationException("Not implemented");
}
}

View File

@ -5,40 +5,27 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.edurt.datacap.common.enums.ServiceState;
import io.edurt.datacap.common.response.CommonResponse;
import io.edurt.datacap.common.response.JwtResponse;
import io.edurt.datacap.common.utils.AiSupportUtils;
import io.edurt.datacap.common.utils.JsonUtils;
import io.edurt.datacap.service.adapter.PageRequestAdapter;
import io.edurt.datacap.service.audit.AuditUserLog;
import io.edurt.datacap.service.body.FilterBody;
import io.edurt.datacap.service.body.UserNameBody;
import io.edurt.datacap.service.body.UserPasswordBody;
import io.edurt.datacap.service.body.UserQuestionBody;
import io.edurt.datacap.service.entity.MenuEntity;
import io.edurt.datacap.service.entity.PageEntity;
import io.edurt.datacap.service.entity.RoleEntity;
import io.edurt.datacap.service.entity.SourceEntity;
import io.edurt.datacap.service.entity.UserChatEntity;
import io.edurt.datacap.service.entity.UserEntity;
import io.edurt.datacap.service.model.AiModel;
import io.edurt.datacap.service.record.TreeRecord;
import io.edurt.datacap.service.repository.RoleRepository;
import io.edurt.datacap.service.repository.SourceRepository;
import io.edurt.datacap.service.repository.UserChatRepository;
import io.edurt.datacap.service.repository.UserRepository;
import io.edurt.datacap.service.security.UserDetailsService;
import io.edurt.datacap.service.service.JwtService;
import io.edurt.datacap.service.service.UserService;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import org.apache.commons.lang.text.StrSubstitutor;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.devlive.sdk.openai.OpenAiClient;
import org.devlive.sdk.openai.entity.CompletionChatEntity;
import org.devlive.sdk.openai.entity.CompletionMessageEntity;
import org.devlive.sdk.openai.entity.UsageEntity;
import org.devlive.sdk.openai.model.CompletionMessageModel;
import org.devlive.sdk.openai.response.CompleteChatResponse;
import org.springframework.core.env.Environment;
import org.springframework.data.domain.Pageable;
import org.springframework.data.redis.core.RedisTemplate;
@ -49,19 +36,14 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
@ -73,7 +55,6 @@ public class UserServiceImpl
{
private final UserRepository userRepository;
private final RoleRepository roleRepository;
private final UserChatRepository userChatRepository;
private final SourceRepository sourceRepository;
private final PasswordEncoder encoder;
private final AuthenticationManager authenticationManager;
@ -81,11 +62,10 @@ public class UserServiceImpl
private final RedisTemplate redisTemplate;
private final Environment environment;
public UserServiceImpl(UserRepository userRepository, RoleRepository roleRepository, UserChatRepository userChatRepository, SourceRepository sourceRepository, PasswordEncoder encoder, AuthenticationManager authenticationManager, JwtService jwtService, RedisTemplate redisTemplate, Environment environment)
public UserServiceImpl(UserRepository userRepository, RoleRepository roleRepository, SourceRepository sourceRepository, PasswordEncoder encoder, AuthenticationManager authenticationManager, JwtService jwtService, RedisTemplate redisTemplate, Environment environment)
{
this.userRepository = userRepository;
this.roleRepository = roleRepository;
this.userChatRepository = userChatRepository;
this.sourceRepository = sourceRepository;
this.encoder = encoder;
this.authenticationManager = authenticationManager;
@ -195,133 +175,11 @@ public class UserServiceImpl
return CommonResponse.failure(ServiceState.USER_NOT_FOUND);
}
UserEntity user = userOptional.get();
user.setThirdConfigure(JsonUtils.toJSON(configure));
user.setChatConfigure(JsonUtils.toJSON(configure));
this.userRepository.save(user);
return CommonResponse.success(user.getId());
}
@Override
public CommonResponse<Object> startChat(UserQuestionBody configure)
{
Optional<UserEntity> userOptional = this.userRepository.findById(UserDetailsService.getUser().getId());
if (!userOptional.isPresent()) {
return CommonResponse.failure(ServiceState.USER_NOT_FOUND);
}
if (!configure.getType().equals("ChatGPT")) {
return CommonResponse.failure("Not supported");
}
UserEntity user = userOptional.get();
UserChatEntity userChat = UserChatEntity.builder()
.question(configure.getContent())
.user(user)
.name(UUID.randomUUID().toString())
.type(configure.getType())
.createTime(Timestamp.valueOf(LocalDateTime.now()))
.isNew(configure.isNewChat())
.build();
String openApiHost = environment.getProperty("datacap.openai.backend");
String openApiToken = environment.getProperty("datacap.openai.token");
String openApiModel = environment.getProperty("datacap.openai.model");
long openApiTimeout = Long.parseLong(environment.getProperty("datacap.openai.timeout"));
if (StringUtils.isNotEmpty(configure.getModel())) {
openApiModel = configure.getModel();
}
// If user-defined configuration, use user configuration information
if (StringUtils.isNotEmpty(user.getThirdConfigure())) {
AiModel aiModel = JsonUtils.toObject(user.getThirdConfigure(), AiModel.class);
if (StringUtils.isNotEmpty(aiModel.getToken())) {
openApiHost = aiModel.getHost();
openApiToken = aiModel.getToken();
}
if (aiModel.getTimeout() > 0) {
openApiTimeout = aiModel.getTimeout();
}
}
// Build the Open AI client
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(openApiTimeout, TimeUnit.SECONDS)
.writeTimeout(openApiTimeout, TimeUnit.SECONDS)
.readTimeout(openApiTimeout, TimeUnit.SECONDS)
.build();
OpenAiClient openAiClient = OpenAiClient.builder()
.apiHost(openApiHost)
.apiKey(openApiToken)
.client(okHttpClient)
.build();
List<String> content = new ArrayList<>();
String forwardContent = configure.getContent();
try {
AiSupportUtils.AiSupportEnum type = AiSupportUtils.AiSupportEnum.valueOf(configure.getTransType());
String replaceContent = AiSupportUtils.getValue(configure.getLocale(), type);
Properties properties = new Properties();
properties.put("sql", configure.getContent());
if (ObjectUtils.isNotEmpty(configure.getEngine())) {
properties.put("engine", configure.getEngine());
}
if (ObjectUtils.isNotEmpty(configure.getError())) {
properties.put("error", configure.getError());
}
StrSubstitutor sub = new StrSubstitutor(properties);
forwardContent = sub.replace(replaceContent);
}
catch (Exception exception) {
log.warn("Ai type not set, ignore .");
}
try {
List<CompletionMessageEntity> messages = new ArrayList<>();
// Extract database history for recording context if source is dialog mode
boolean saved = false;
if (StringUtils.isNotEmpty(configure.getFrom()) && configure.getFrom().equalsIgnoreCase("chat")) {
if (!configure.isNewChat()) {
this.userChatRepository.findTop5ByUserOrderByIdDesc(UserDetailsService.getUser())
.stream()
.sorted(Comparator.comparing(UserChatEntity::getId))
.forEach(userChatHistory -> {
CompletionMessageEntity question = CompletionMessageEntity.builder()
.role(CompletionMessageModel.USER.getName())
.content(userChatHistory.getQuestion())
.build();
messages.add(question);
CompletionMessageEntity answer = CompletionMessageEntity.builder()
.role(CompletionMessageModel.ASSISTANT.getName())
.content(userChatHistory.getAnswer())
.build();
messages.add(answer);
});
}
saved = true;
}
CompletionMessageEntity message = CompletionMessageEntity.builder()
.role(CompletionMessageModel.USER.getName())
.content(forwardContent)
.build();
messages.add(message);
CompletionChatEntity chatCompletion = CompletionChatEntity.builder()
.messages(messages)
.model(openApiModel)
.build();
CompleteChatResponse chatCompletionResponse = openAiClient.createChatCompletion(chatCompletion);
chatCompletionResponse.getChoices()
.forEach(e -> content.add(e.getMessage().getContent()));
userChat.setEndTime(Timestamp.valueOf(LocalDateTime.now()));
userChat.setAnswer(String.join("\n", content));
userChat.setModel(chatCompletionResponse.getModel());
UsageEntity usage = chatCompletionResponse.getUsage();
userChat.setPromptTokens(usage.getPromptTokens());
userChat.setCompletionTokens(usage.getCompletionTokens());
userChat.setTotalTokens(usage.getTotalTokens());
if (saved) {
this.userChatRepository.save(userChat);
}
return CommonResponse.success(userChat);
}
catch (Exception e) {
return CommonResponse.failure(e.getMessage());
}
}
@Override
public CommonResponse<List<Object>> getSugs(Long id)
{

View File

@ -13,6 +13,7 @@
"@fortawesome/fontawesome-svg-core": "^6.4.0",
"@fortawesome/free-solid-svg-icons": "^6.4.0",
"@fortawesome/vue-fontawesome": "^3.0.0-5",
"@kangc/v-md-editor": "^2.3.15",
"@types/nprogress": "^0.2.0",
"@types/watermark-dom": "^2.3.1",
"ag-grid-community": "^29.3.5",
@ -21,11 +22,13 @@
"core-js": "^3.8.3",
"echarts": "^5.4.0",
"export-to-csv": "^0.2.1",
"highlight.js": "^11.8.0",
"install": "^0.13.0",
"lodash": "^4.17.21",
"monaco-editor-vue3": "^0.1.6",
"monaco-editor-webpack-plugin": "^7.0.1",
"nprogress": "^0.2.0",
"prismjs": "^1.29.0",
"squel": "^5.13.0",
"view-ui-plus": "^1.3.1",
"vue": "^3.2.13",

View File

@ -0,0 +1,42 @@
<template>
<div>
<Spin fix
:show="show">
<Icon type="ios-loading"
size=18
class="spin-icon-load">
</Icon>
<div>Loading</div>
</Spin>
</div>
</template>
<script>
import {defineComponent} from "vue";
export default defineComponent({
name: "CircularLoading",
props: {
show: {
type: Boolean,
default: () => false
}
}
})
</script>
<style scoped>
.spin-icon-load {
animation: ani-spin 1s linear infinite;
}
@keyframes ani-spin {
from {
transform: rotate(0deg);
}
50% {
transform: rotate(180deg);
}
to {
transform: rotate(360deg);
}
}
</style>

View File

@ -0,0 +1,54 @@
<template>
<div>
<div>
<VMdPreview v-if="content"
:text="content">
</VMdPreview>
</div>
</div>
</template>
<script>
import {defineComponent} from "vue";
import VMdPreview from '@kangc/v-md-editor/lib/preview'
import '@kangc/v-md-editor/lib/style/preview.css';
import vuepressTheme from '@kangc/v-md-editor/lib/theme/vuepress.js';
import '@kangc/v-md-editor/lib/theme/style/vuepress.css';
import Prism from 'prismjs';
import 'prismjs/components/prism-json';
import 'prismjs/components/prism-java';
import 'prismjs/components/prism-c';
import 'prismjs/components/prism-css'
import 'prismjs/components/prism-typescript'
import 'prismjs/components/prism-javascript'
import 'prismjs/components/prism-http'
import 'prismjs/components/prism-powershell'
import 'prismjs/components/prism-antlr4'
import 'prismjs/components/prism-scala'
import 'prismjs/components/prism-sass'
import 'prismjs/components/prism-sql'
import 'prismjs/components/prism-json5'
import createLineNumberPlugin from '@kangc/v-md-editor/lib/plugins/line-number/index';
import createCopyCodePlugin from '@kangc/v-md-editor/lib/plugins/copy-code/index';
import '@kangc/v-md-editor/lib/plugins/copy-code/copy-code.css';
VMdPreview.use(vuepressTheme, {
Prism
});
VMdPreview.use(createLineNumberPlugin())
VMdPreview.use(createCopyCodePlugin())
export default defineComponent({
name: "MarkdownView",
props: {
content: {
type: Boolean,
default: () => false
}
},
components: {VMdPreview}
})
</script>
<style scoped>
</style>

View File

@ -26,7 +26,7 @@ class Entity<T extends EntityProperties = EntityProperties>
public static default(): Entity<Pick<EntityProperties, 'id' | 'name' | 'description'>>
{
return new Entity({
id: 1,
id: 0,
name: 'admin',
description: 'Administrator',
});
@ -35,10 +35,15 @@ class Entity<T extends EntityProperties = EntityProperties>
/**
* Get the value of the specified property.
*/
public get<K extends keyof T>(prop: K): T[K]
public field<K extends keyof T>(prop: K): T[K]
{
return this.props[prop];
}
public get(): T
{
return this.props;
}
}
class EntityBuilder<P extends EntityProperties = EntityProperties>

View File

@ -123,4 +123,5 @@ export default {
chat: 'Chat',
timeout: 'Timeout',
seconds: 'Seconds',
avatar: 'Avatar',
}

View File

@ -8,5 +8,7 @@ export default {
accountSetting: 'Account Setting',
changeUsername: 'Change the username',
newUsername: 'New username',
log: 'Login Log'
log: 'Login Log',
changeChatGpt: 'Change the chatgpt',
contentCount: 'Content Count',
}

View File

@ -122,5 +122,6 @@ export default {
new: '新',
chat: '对话',
timeout: '超时时间',
seconds: '秒'
seconds: '秒',
avatar: '头像'
}

View File

@ -8,5 +8,7 @@ export default {
accountSetting: '账号设置',
changeUsername: '修改用户名',
newUsername: '新用户名',
log: '登录日志'
log: '登录日志',
changeChatGpt: '修改 ChatGPT',
contentCount: '上下文关联数'
}

View File

@ -3,6 +3,7 @@ export interface User
username: string;
thirdConfigure: ThirdConfigure;
createTime?: string;
id?: number;
}
export class UserQuestion
@ -38,5 +39,6 @@ export class ThirdConfigure
token = '';
host = '';
model = '';
timeout: number = null;
timeout = 30;
contentCount = 5
}

View File

@ -52,11 +52,14 @@ export abstract class BaseService<T>
}
}
deleteById(id: number): Promise<ResponseModel>
{
return new HttpCommon().delete(`${this.baseUrl}/${id}`);
}
/**
* Filter the corresponding data according to the name
* @param name
*/
abstract getByName<T>(name: string): Promise<ResponseModel>;
abstract deleteById(id: number): Promise<ResponseModel>;
}

View File

@ -0,0 +1,26 @@
import {BaseService} from "@/services/BaseService";
import {ResponseModel} from "@/model/ResponseModel";
import {HttpCommon} from "@/common/HttpCommon";
const baseUrl = '/api/v1/chat';
export class ChatService
extends BaseService<any>
{
constructor()
{
super(baseUrl);
}
getMessages(id: number): Promise<ResponseModel>
{
return new HttpCommon().get(`${baseUrl}/${id}/messages`);
}
getByName<T>(name: string): Promise<ResponseModel>
{
return Promise.resolve(undefined);
}
}
export default new ChatService();

View File

@ -0,0 +1,27 @@
import {BaseService} from "@/services/BaseService";
import {ResponseModel} from "@/model/ResponseModel";
import {UserQuestion} from "@/model/User";
import {HttpCommon} from "@/common/HttpCommon";
const baseUrl = '/api/v1/message';
export class MessageService
extends BaseService<any>
{
constructor()
{
super(baseUrl);
}
getByName<T>(name: string): Promise<ResponseModel>
{
return Promise.resolve(undefined);
}
aiReply(configure: UserQuestion): Promise<ResponseModel>
{
return new HttpCommon().post(baseUrl + '/ai/reply', configure);
}
}
export default new MessageService();

View File

@ -3,7 +3,6 @@ import {ResponseModel} from "@/model/ResponseModel";
import {UserPassword} from "@/model/UserPassword";
import {UserName} from "@/model/UserName";
import {Filter} from "@/model/Filter";
import {UserQuestion} from "@/model/User";
const baseUrl = "/api/v1/user";
@ -29,11 +28,6 @@ class UserService
return new HttpCommon().put(baseUrl + '/changeThirdConfigure', configure);
}
startChat(configure: UserQuestion): Promise<ResponseModel>
{
return new HttpCommon().post(baseUrl + '/startChat', configure);
}
getLogs(filter: Filter): Promise<ResponseModel>
{
return new HttpCommon().post(baseUrl + '/log', filter);

View File

@ -1,126 +1,171 @@
<template>
<div>
<Card dis-hover
style="width:100%; minHeight: 150px;">
<template #title>
{{ $t('common.chatgpt') }} &nbsp;
<Select v-model="gptModel"
style="width:200px">
<Option v-for="model in models"
:value="model"
v-bind:key="model">
{{ model }}
</Option>
</Select>
</template>
<template #extra>
<Tooltip :content="$t('common.settings')" transfer>
<Button type="primary" size="small" shape="circle" icon="ios-cog" @click="handlerVisibleDetail(true)"></Button>
</Tooltip>
</template>
<div>
<Layout style="background-color: #FFFFFF;">
<Content style="padding: 0px 0px 0px 10px;">
<div ref="scrollDiv" style="height: 300px; max-height: 300px; overflow: auto;">
<div v-for="item in userQuestionItems"
:key="item">
<Divider :orientation="item.isSelf ? 'left' : 'right'">
<Avatar v-if="item.isSelf" icon="ios-person"></Avatar>
<Avatar v-else icon="md-ionitron" style="background-color: #87d068;"></Avatar>
</Divider>
<div :style="{margin: '0px 10px', float: item.isSelf ? '' : 'right'}">
<Card dis-hover
:bordered="false">
<VMarkdownView v-if="item.content"
:mode="'light'"
:content="item.content.answer">
</VMarkdownView>
<div v-if="!item.isSelf"
style="margin-top: 5px; float: right;">
Model:
<Text strong>{{ item.content.model }}</Text>
<Divider type="vertical"/>
Prompt Tokens:
<CountUp :end="item.content.promptTokens"
v-font="24">
</CountUp>
<Divider type="vertical"/>
Completion Tokens:
<CountUp :end="item.content.completionTokens"
v-font="24">
</CountUp>
<Divider type="vertical"/>
Total Tokens:
<CountUp :end="item.content.totalTokens"
v-font="24">
</CountUp>
</div>
</Card>
</div>
</div>
<Layout>
<Sider style="background-color: #FFFFFF">
<Card dis-hover
padding="0">
<template #title>
{{ $t('common.chat') }}
</template>
<template #extra>
<Tooltip :content="$t('common.create') + $t('common.chat')">
<Button type="primary"
shape="circle"
size="small"
style="margin-top: -10px;"
icon="md-add"
@click="handlerCreateChat(true)">
</Button>
</Tooltip>
</template>
<RadioGroup v-model="selectedChat"
vertical
style="margin: 3px 3px 3px 3px; height: 500px; width: 100%; overflow: auto;"
@on-change="handlerChangeChat">
<Radio v-for="item in chats"
:key="item.id"
:label="item"
border
style="margin-top: 3px; padding: 3px 5px; height: 40px;">
<Avatar v-if="item.avatar"
:src="item.avatar">
</Avatar>
<Avatar v-else>
{{ item.name }}
</Avatar>
{{ item.name }}
</Radio>
</RadioGroup>
<CircularLoading v-if="loadingChats"
:show="loadingChats">
</CircularLoading>
</Card>
</Sider>
<Content style="margin-left: 3px;">
<Card v-if="selectedChat"
dis-hover
style="width:100%; minHeight: 150px;">
<template #title>
<Text strong>{{ $t('common.chat') }}</Text>
: {{ selectedChat.name }} &nbsp;
</template>
<template #extra>
<div style="margin-top: -13px;">
Prompt Tokens:
<CountUp :end="promptTokens"
v-font="24">
</CountUp>
<Divider type="vertical"/>
Completion Tokens:
<CountUp :end="completionTokens"
v-font="24">
</CountUp>
<Divider type="vertical"/>
Total Tokens:
<CountUp :end="totalTokens"
v-font="24">
</CountUp>
</div>
</Content>
<Divider></Divider>
<Footer style="background-color: #FFFFFF;">
<Row>
<Col span="20">
<Input v-model="userQuestionContext"
type="textarea"
:autosize="{minRows: 2,maxRows: 5}">
</Input>
</Col>
<Col span="2" offset="1">
<Button type="primary"
icon="md-send"
:disabled="!userQuestionContext"
:loading="startChatLoading"
@click="handlerStartChat()">
{{ $t('common.send') }}
</Button>
</Col>
</Row>
</Footer>
</Layout>
</div>
<Spin size="large" fix :show="loading"></Spin>
</Card>
<Modal v-if="visibleModel" v-model="visibleModel" :title="$t('common.chatgpt') +' ' + $t('common.settings')"
:mask-closable="false" :closable="false">
<Form :label-width="80">
<FormItem :label="$t('common.token')">
<Input v-model="userInfo.thirdConfigure.token" type="text" placeholder="example: sk-xxxx"/>
</FormItem>
<FormItem :label="$t('common.proxy')">
<Input v-model="userInfo.thirdConfigure.host" type="text"/>
</FormItem>
<FormItem :label="$t('common.timeout')">
<InputNumber v-model="userInfo.thirdConfigure.timeout"/>
<Text strong
style="margin-left: 5px;">
{{ $t('common.seconds') }}
</Text>
</FormItem>
</Form>
<template #footer>
<Space>
<Button type="error" @click="handlerVisibleDetail(false)">{{ $t('common.cancel') }}</Button>
<Button type="primary" icon="md-document" @click="handlerSave()">{{ $t('common.save') }}</Button>
</Space>
</template>
</Modal>
</template>
<Layout style="background-color: #FFFFFF;">
<Content style="padding: 0px 0px 0px 10px;">
<div ref="scrollDiv" style="height: 300px; max-height: 300px; overflow: auto;">
<div v-for="item in messages"
:key="item">
<Divider :orientation="item.type === 'question' ? 'left' : 'right'">
<Avatar v-if="item.type === 'question'">
{{ userInfo.username }}
</Avatar>
<Avatar v-else
icon="md-ionitron"
style="background-color: #87d068;">
</Avatar>
</Divider>
<div :style="{margin: '0px 10px', float: item.type === 'question' ? '' : 'right'}">
<Card dis-hover
:bordered="false"
padding="0">
<MarkdownView v-if="item.content"
:content="item.content">
</MarkdownView>
<div v-if="item.type === 'answer'"
style="margin-top: 5px; float: right;">
Model:
<Text strong>{{ item.model }}</Text>
<Divider type="vertical"/>
Prompt Tokens:
<CountUp :end="item.promptTokens"
v-font="24">
</CountUp>
<Divider type="vertical"/>
Completion Tokens:
<CountUp :end="item.completionTokens"
v-font="24">
</CountUp>
<Divider type="vertical"/>
Total Tokens:
<CountUp :end="item.totalTokens"
v-font="24">
</CountUp>
</div>
</Card>
</div>
</div>
<CircularLoading :show="loadingMessages">
</CircularLoading>
</div>
</Content>
<Divider></Divider>
<Footer style="background-color: #FFFFFF;">
<Row>
<Col span="20">
<Input v-model="questionContext"
type="textarea"
:disabled="sendMessage"
:autosize="{minRows: 2,maxRows: 5}">
</Input>
</Col>
<Col span="2" offset="1">
<Button type="primary"
icon="md-send"
:disabled="!questionContext"
:loading="sendMessage"
@click="handlerSendMessage()">
{{ $t('common.send') }}
</Button>
</Col>
</Row>
</Footer>
</Layout>
</Card>
<Card v-else
dis-hover>
<div style="text-align:center; margin: 248px 0px;">
No selection chat
</div>
</Card>
</Content>
</Layout>
<CreateChat v-if="createChatVisible"
:is-visible="createChatVisible"
:user-id="userInfo.id"
@close="handlerCreateChat(false)">
</CreateChat>
</div>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
import UserService from "@/services/UserService";
import {ThirdConfigure, User, UserAnswer, UserQuestion, UserQuestionItem} from '@/model/User';
import {VMarkdownView} from 'vue3-markdown'
import 'vue3-markdown/dist/style.css'
import {InputNumber} from "view-ui-plus";
import CreateChat from "@/views/common/chat/components/CreateChat.vue";
import ChatService from '@/services/ChatService';
import {Filter} from '@/model/Filter';
import {AuthResponse} from '@/model/AuthResponse';
import {TokenCommon} from '@/common/TokenCommon';
import CircularLoading from "@/components/loading/CircularLoading.vue";
import MessageService from "@/services/MessageService";
import MarkdownView from "@/components/markdown/MarkdownView.vue";
export default defineComponent({
components: {InputNumber, VMarkdownView},
components: {MarkdownView, CircularLoading, CreateChat},
created()
{
this.handlerInitialize()
@ -128,88 +173,82 @@ export default defineComponent({
data()
{
return {
loading: false,
visibleModel: false,
startChatLoading: false,
userInfo: null as User,
userQuestionContext: null,
userQuestionItems: null as UserQuestionItem[],
gptModel: 'gpt-3.5-turbo-0613',
models: ['gpt-3.5-turbo', 'gpt-3.5-turbo-0301', 'gpt-3.5-turbo-0613', 'gpt-3.5-turbo-16k', 'gpt-3.5-turbo-16k-0613', 'gpt-4', 'gpt-4-0314', 'gpt-4-0613']
chats: [],
loadingChats: false,
selectedChat: null,
userInfo: null as AuthResponse,
messages: [],
loadingMessages: false,
createChatVisible: false,
questionContext: null,
sendMessage: false,
promptTokens: 0,
completionTokens: 0,
totalTokens: 0
}
},
methods: {
handlerInitialize()
{
this.userQuestionItems = [];
this.loading = true;
UserService.getInfo()
this.userInfo = TokenCommon.getAuthUser()
this.loadingChats = true;
const filter = new Filter();
filter.size = 100
filter.orders = [{column: 'createTime', order: 'desc'}];
ChatService.getAll(filter)
.then(response => {
if (response.status) {
this.userInfo = response.data;
if (this.userInfo.thirdConfigure) {
this.userInfo.thirdConfigure = JSON.parse(this.userInfo.thirdConfigure);
}
else {
this.userInfo.thirdConfigure = new ThirdConfigure();
}
}
this.chats = response.data?.content
})
.finally(() => {
this.loading = false;
this.loadingChats = false;
});
},
handlerVisibleDetail(value: boolean)
handlerCreateChat(opened: boolean)
{
this.visibleModel = value;
if (!value) {
this.handlerInitialize();
this.createChatVisible = opened;
if (!opened) {
this.handlerInitialize()
}
},
handlerSave()
handlerChangeChat()
{
UserService.changeThirdConfigure(this.userInfo.thirdConfigure)
this.loadingMessages = true;
ChatService.getMessages(this.selectedChat.id)
.then(response => {
this.$Message.success(response.message);
this.handlerVisibleDetail(false);
this.messages = response.data
})
.finally(() => {
this.loading = false;
this.loadingMessages = false;
this.counterToken()
this.handlerGoBottom()
});
},
handlerStartChat()
handlerSendMessage()
{
const userQuestion = new UserQuestion();
userQuestion.type = 'ChatGPT';
userQuestion.content = this.userQuestionContext;
userQuestion.from = 'chat';
userQuestion.newChat = this.userQuestionItems.length > 0 ? false : true;
userQuestion.model = this.gptModel;
const answer = new UserAnswer();
answer.answer = this.userQuestionContext;
const question = new UserQuestionItem();
question.content = answer;
question.isSelf = true;
this.userQuestionItems.push(question);
this.handlerGoBottom();
this.startChatLoading = true;
this.userQuestionContext = null;
UserService.startChat(userQuestion)
this.sendMessage = true;
const message = {
content: this.questionContext,
user: this.userInfo,
chat: this.selectedChat,
type: 'question'
}
this.messages.push(message)
this.handlerGoBottom()
this.questionContext = null;
MessageService.saveOrUpdate(message)
.then(response => {
if (response.status) {
const answer = new UserQuestionItem();
answer.content = response.data;
answer.isSelf = false;
this.userQuestionItems.push(answer);
this.handlerGoBottom();
this.messages.push(response.data)
}
else {
this.$Message.error(response.message);
this.$Message.error(response.message)
}
})
.finally(() => {
this.startChatLoading = false;
});
this.sendMessage = false
this.counterToken()
this.handlerGoBottom()
})
},
handlerGoBottom()
{
@ -217,12 +256,16 @@ export default defineComponent({
setTimeout(() => {
scrollElem.scrollTo({top: scrollElem.scrollHeight, behavior: 'smooth'});
}, 0);
},
counterToken()
{
const answers = this.messages.filter(message => message.type === 'answer')
this.promptTokens = answers.reduce((sum, message) => sum + message.promptTokens, 0)
this.completionTokens = answers.reduce((sum, message) => sum + message.completionTokens, 0)
this.totalTokens = answers.reduce((sum, message) => sum + message.totalTokens, 0)
}
}
});
</script>
<style scoped>
.content {
background-color: #FFFFFF;
}
</style>

View File

@ -0,0 +1,104 @@
<template>
<Modal v-model="visible"
:title="$t('common.create') + $t('common.chat')"
width="30%"
:closable="false"
:mask-closable="false"
@cancel="handlerCancel()">
<Form :model="fromState">
<FormItem :label="$t('common.name')" prop="name">
<Input v-model="fromState['name']"/>
</FormItem>
<FormItem :label="$t('common.avatar')" prop="avatar">
<Input v-model="fromState['avatar']"/>
</FormItem>
<FormItem :label="$t('common.description')" prop="description">
<Input v-model="fromState['description']"
type="textarea"
:rows="2">
</Input>
</FormItem>
</Form>
<template #footer>
<Button key="cancel" type="error" :disabled="saving" size="small" @click="handlerCancel()">
{{ $t('common.cancel') }}
</Button>
<Button type="primary" size="small" :loading="saving" @click="handlerSave()">
{{ $t('common.save') }}
</Button>
</template>
</Modal>
</template>
<script lang="ts">
import {defineComponent} from "vue";
import defaultEntity from "@/entity/CommonEntity";
import RoomService from "@/services/ChatService";
export default defineComponent({
name: "CreateChat",
props: {
isVisible: {
type: Boolean,
default: () => false
},
userId: {
type: Number
}
},
data()
{
return {
saving: false,
fromState: null
}
},
created()
{
this.fromState = defaultEntity.set('name', null)
.set('avatar', null)
.set('description', null)
.set('user', null)
.build()
.get()
},
methods: {
handlerSave()
{
this.saving = true;
this.fromState['user'] = {
id: this.userId
}
RoomService.saveOrUpdate(this.fromState)
.then(response => {
if (response.status) {
this.$Message.success("successful");
this.handlerCancel();
}
else {
this.$Message.error(response.message);
}
})
.finally(() => {
this.saving = false;
});
},
handlerCancel()
{
this.fromState = null;
this.visible = false;
}
},
computed: {
visible: {
get(): boolean
{
return this.isVisible;
},
set(value: boolean)
{
this.$emit('close', value);
}
}
}
});
</script>

View File

@ -26,6 +26,7 @@ import {User, UserQuestion} from "@/model/User";
import {VMarkdownView} from 'vue3-markdown'
import 'vue3-markdown/dist/style.css'
import {Skeleton} from "view-ui-plus";
import MessageService from "@/services/MessageService";
export default defineComponent({
name: 'QueryAiHelp',
@ -92,10 +93,10 @@ export default defineComponent({
userQuestion.engine = this.engine;
userQuestion.error = this.error;
userQuestion.locale = this.$i18n.locale;
UserService.startChat(userQuestion)
MessageService.aiReply(userQuestion)
.then(response => {
if (response.status) {
this.finalContent = response.data.answer;
this.finalContent = response.data.content;
}
else {
this.$Message.error(response.message);

View File

@ -1,6 +1,8 @@
<template>
<div>
<Card style="width:100%; minHeight: 150px;">
<Card
dis-hover
style="width:100%; minHeight: 150px;">
<template #title>
{{ $t('setting.accountSetting') }}
</template>
@ -20,6 +22,11 @@
{{ $t('setting.changePassword') }}
</Button>
</FormItem>
<FormItem :label="$t('common.chatgpt')">
<Button type="text" style="float: right;" @click="handlerChatGPT(true)">
{{ $t('setting.changeChatGpt') }}
</Button>
</FormItem>
</Form>
</Content>
</Layout>
@ -43,7 +50,14 @@
<Button type="primary" :loading="loading" @click="handlerChangePassword()">{{ $t('common.save') }}</Button>
</template>
</Modal>
<ChangeUsername v-if="changeUsername" :isVisible="changeUsername" @close="changeUsername = false"/>
<ChangeUsername v-if="changeUsername"
:isVisible="changeUsername"
@close="changeUsername = false">
</ChangeUsername>
<ChangeChatGpt v-if="changeChatGpt"
:is-visible="changeChatGpt"
@close="handlerChatGPT(false)">
</ChangeChatGpt>
</div>
</template>
<script lang="ts">
@ -53,9 +67,10 @@ import {UserPassword} from "@/model/UserPassword";
import Common from "@/common/Common";
import router from "@/router";
import ChangeUsername from "@/views/user/profile/components/ChangeUsername.vue";
import ChangeChatGpt from "@/views/user/profile/components/ChangeChatGpt.vue";
export default defineComponent({
components: {ChangeUsername},
components: {ChangeChatGpt, ChangeUsername},
setup()
{
const formState = reactive<UserPassword>({
@ -72,6 +87,7 @@ export default defineComponent({
return {
changePasswordVisible: false,
changeUsername: false,
changeChatGpt: false,
loading: false
}
},
@ -107,6 +123,10 @@ export default defineComponent({
.finally(() => {
this.loading = false;
});
},
handlerChatGPT(opened: boolean)
{
this.changeChatGpt = opened
}
}
});

View File

@ -0,0 +1,137 @@
<template>
<div>
<Modal v-model="visible"
:title="$t('common.chatgpt')"
:closable="false"
:mask-closable="false"
@cancel="handlerCancel()">
<Form :model="chatConfigure"
:label-width="100">
<FormItem prop="host"
:label="$t('common.proxy')">
<Input type="text"
v-model="chatConfigure.host">
</Input>
</FormItem>
<FormItem name="token"
:label="$t('common.token')">
<Input type="password"
v-model="chatConfigure.token">
</Input>
</FormItem>
<FormItem name="timeout"
:label="$t('common.timeout')">
<InputNumber v-model="chatConfigure.timeout"
min="1">
</InputNumber>
</FormItem>
<FormItem name="contentCount"
:label="$t('setting.contentCount')">
<InputNumber v-model="chatConfigure.contentCount"
min="1"
max="10">
</InputNumber>
</FormItem>
<CircularLoading v-if="loadingUserInfo"
:show="loadingUserInfo">
</CircularLoading>
</Form>
<template #footer>
<Button danger
@click="handlerCancel">
{{ $t('common.cancel') }}
</Button>
<Button type="primary"
:loading="loadingChange"
@click="handlerSave()">
{{ $t('common.save') }}
</Button>
</template>
</Modal>
</div>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
import UserService from "@/services/UserService";
import CircularLoading from "@/components/loading/CircularLoading.vue";
import {ThirdConfigure} from "@/model/User";
export default defineComponent({
name: 'ChangeChatGpt',
components: {CircularLoading},
props: {
isVisible: {
type: Boolean,
default: () => false
}
},
data()
{
return {
loadingUserInfo: false,
loadingChange: false,
chatConfigure: {} as ThirdConfigure
}
},
created()
{
this.handlerInitialize()
},
methods: {
handlerInitialize()
{
this.loadingUserInfo = true
UserService.getInfo()
.then(response => {
if (response.data.chatConfigure) {
this.chatConfigure = JSON.parse(response.data.chatConfigure)
}
else {
this.chatConfigure = new ThirdConfigure()
}
})
.finally(() => {
this.loadingUserInfo = false
})
},
handlerSave()
{
this.loadingChange = true;
UserService.changeThirdConfigure(this.chatConfigure)
.then((response) => {
if (response.status) {
this.$Message.success('Success');
this.handlerCancel()
}
else {
this.$Message.error(response.message);
}
})
.finally(() => {
this.loadingChange = false;
});
},
handlerCancel()
{
this.visible = false;
},
},
computed: {
visible: {
get(): boolean
{
return this.isVisible;
},
set(value: boolean)
{
this.$emit('close', value);
}
}
}
});
</script>
<style scoped>
.content {
background-color: #FFFFFF;
}
</style>