Skip to content

Commit

Permalink
integrate api
Browse files Browse the repository at this point in the history
  • Loading branch information
Germey committed Sep 30, 2023
1 parent d33079c commit 1d6b00c
Show file tree
Hide file tree
Showing 10 changed files with 293 additions and 34 deletions.
15 changes: 13 additions & 2 deletions src/components/chat/InputBox.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
<template>
<div class="input-box">
<el-input v-model="value" :rows="2" type="textarea" placeholder="Please input" />
<el-input
v-model="value"
:rows="2"
type="textarea"
placeholder="Please input"
@keydown.enter.exact.prevent="onSubmit"
/>
</div>
</template>

Expand All @@ -23,7 +29,7 @@ export default defineComponent({
required: true
}
},
emits: ['update:modelValue'],
emits: ['update:modelValue', 'submit'],
data() {
return {
value: this.modelValue
Expand All @@ -38,6 +44,11 @@ export default defineComponent({
this.value = val;
}
}
},
methods: {
onSubmit() {
this.$emit('submit', this.value);
}
}
});
</script>
Expand Down
3 changes: 2 additions & 1 deletion src/components/chat/Message.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { ElImage, ElButton } from 'element-plus';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import copy from 'copy-to-clipboard';
import MarkdownRenderer from '@/components/common/MarkdownRenderer.vue';
import { IChatMessage } from '@/operators';
interface IData {
copied: boolean;
Expand All @@ -31,7 +32,7 @@ export default defineComponent({
},
props: {
message: {
type: Object as () => IMessage,
type: Object as () => IChatMessage,
required: true
}
},
Expand Down
5 changes: 5 additions & 0 deletions src/operators/chat/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const API_ID_CHATGPT = '3333';
export const API_ID_CHATGPT4 = '3333';
export const API_ID_CHATGPT_BROWSING = '3333';
export const API_ID_CHATGPT4_BROWSING = '3333';
export const API_ID_CHATGPT_16k = '3333';
2 changes: 2 additions & 0 deletions src/operators/chat/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './models';
export * from './operator';
52 changes: 52 additions & 0 deletions src/operators/chat/models.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
export interface IError {
code: string;
detail?: string;
}

export enum ChatMessageState {
PENDING = 'pending',
ANSWERING = 'answering',
FINISHED = 'finished',
FAILED = 'failed'
}

export const ROLE_SYSTEM = 'system';
export const ROLE_ASSISTANT = 'assistant';
export const ROLE_USER = 'user';

export interface IChatMessage {
state?: ChatMessageState;
content: string;
role?: typeof ROLE_SYSTEM | typeof ROLE_ASSISTANT | typeof ROLE_USER;
error?: IError;
}

export const CHAT_MODEL_CHATGPT = 'chatgpt';
export const CHAT_MODEL_CHATGPT4 = 'chatgpt4';
export const CHAT_MODEL_CHATGPT_16K = 'chatgpt-16k';
export const CHAT_MODEL_CHATGPT_BROWSING = 'chatgpt-browsing';
export const CHAT_MODEL_CHATGPT4_BROWSING = 'chatgpt4-browsing';

export type IChatModel =
| typeof CHAT_MODEL_CHATGPT
| typeof CHAT_MODEL_CHATGPT4
| typeof CHAT_MODEL_CHATGPT_16K
| typeof CHAT_MODEL_CHATGPT_BROWSING
| typeof CHAT_MODEL_CHATGPT4_BROWSING;

export interface IChatRequest {
question: string;
stateful?: boolean;
conversation_id?: string;
}

export interface IChatResponse {
answer: string;
delta_answer: string;
conversation_id?: string;
}

export interface IChatOptions {
stream?: (response: IChatResponse) => void;
api_id: string;
}
40 changes: 40 additions & 0 deletions src/operators/chat/operator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import axios, { AxiosResponse, AxiosRequestConfig } from 'axios';
import { IChatOptions, IChatRequest, IChatResponse } from './models';
import store from '@/store';
import { IApplication } from '@/store/models';
class ChatOperator {
async request(data: IChatRequest, config?: IChatOptions): Promise<AxiosResponse<IChatResponse>> {
const applications: IApplication[] = store.getters.applications;
// find related application
const application = applications?.filter((application: IApplication) => {
return (application.api_id = config?.api_id);
})?.[0];
if (!application) {
return Promise.reject('no application');
}
const token = application?.credential?.token;
if (!token) {
return Promise.reject('no token');
}
return await axios.post(`/chatgpt`, data, {
headers: {
authorization: `Bearer ${token}`,
accept: 'application/x-ndjson',
'content-type': 'application/json'
},
onDownloadProgress: (event) => {
const response = event.target.response;
const lines = response.split('\r\n').filter((line: string) => !!line);
const lastLine = lines[lines.length - 1];
if (lastLine) {
const jsonData = JSON.parse(lastLine);
if (config?.stream) {
config?.stream(jsonData as IChatResponse);
}
}
}
});
}
}

export const chatOperator = new ChatOperator();
1 change: 1 addition & 0 deletions src/operators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './instance';
export * from './application';
export * from './user';
export * from './api';
export * from './chat';
113 changes: 103 additions & 10 deletions src/pages/chat/Conversation.vue
Original file line number Diff line number Diff line change
@@ -1,49 +1,142 @@
<template>
<div class="page">
<div v-for="(message, messageIndex) in messages" :key="messageIndex">
<message :message="message" class="message" />
<model-selector v-model="model" class="model-selector" />
<div class="main">
<introduction v-if="messages && messages.length === 0" />
<div v-else class="messages">
<div v-for="(message, messageIndex) in messages" :key="messageIndex">
<message :message="message" class="message" />
</div>
</div>
</div>
<div class="bottom">
<input-box v-model="question" @submit="onSubmitQuestion" />
</div>
</div>
</template>

<script lang="ts">
import { IMessage, ROLE_ASSISTANT, ROLE_SYSTEM, ROLE_USER } from '@/operators/message/models';
import { IMessage, ROLE_ASSISTANT, ROLE_USER } from '@/operators/message/models';
import { defineComponent } from 'vue';
import Message from '@/components/chat/Message.vue';
import { IChatModel, IChatMessage, CHAT_MODEL_CHATGPT, IChatResponse } from '@/operators';
import InputBox from '@/components/chat/InputBox.vue';
import ModelSelector from '@/components/chat/ModelSelector.vue';
import { chatOperator } from '@/operators';
export interface IData {
messages: IMessage[];
messages: IChatMessage[];
model: IChatModel;
question: '';
}
export default defineComponent({
name: 'ChatConversation',
components: {
// Introduction,
// InputBox,
// ModelSelector,
InputBox,
ModelSelector,
Message
},
data(): IData {
return {
question: '',
model: CHAT_MODEL_CHATGPT,
messages: [
{
role: ROLE_USER,
content: 'hello'
},
{
role: ROLE_ASSISTANT,
content: 'hello, how can I assist you?'
content: '## ok \n\n hello, how can I assist you? \n ```python\nprint("hello")\n```'
},
{
role: ROLE_USER,
content: 'hello'
},
{
role: ROLE_ASSISTANT,
content: '## ok \n\n hello, how can I assist you? \n ```python\nprint("hello")\n```'
},
{
role: ROLE_USER,
content: 'hello'
},
{
role: ROLE_ASSISTANT,
content: '## ok \n\n hello, how can I assist you? \n ```python\nprint("hello")\n```'
},
{
role: ROLE_USER,
content: 'hello'
},
{
role: ROLE_ASSISTANT,
content: '## ok \n\n hello, how can I assist you? \n ```python\nprint("hello")\n```'
},
{
role: ROLE_USER,
content: 'hello'
},
{
role: ROLE_ASSISTANT,
content: '## ok \n\n hello, how can I assist you? \n ```python\nprint("hello")\n```'
}
]
};
},
computed: {
conversationId(): string | undefined {
return this.$route.params.id?.toString();
}
},
methods: {
onSubmitQuestion() {
this.messages.push({
content: this.question,
role: ROLE_USER
});
this.question = '';
},
async onFetchAnswer() {
const apiId = this.model;
const { data: response }: IChatResponse = await chatOperator.request(
{
question: this.question,
conversation_id: this.conversationId,
stateful: true
},
{
api_id: apiId,
stream: (response: IChatResponse) => {
console.log('response', response);
}
}
);
console.log('this.response', response);
}
}
});
</script>

<style lang="scss" scoped>
.page {
padding: 20px;
.message {
margin-bottom: 10px;
padding: 15px;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
height: 100%;
.main {
flex: 1;
width: 100%;
overflow-y: scroll;
padding: 15px 0;
}
.bottom {
width: 100%;
height: 70px;
}
}
</style>
47 changes: 26 additions & 21 deletions src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,64 @@ import { createStore, ActionContext } from 'vuex';
import createPersistedState from 'vuex-persistedstate';
import { removeCookies } from '@/utils/cookie';
import { ENDPOINT } from '@/constants';

export interface ISetting {
stream?: boolean;
endpoint?: string;
}

export interface IState {
accessToken: string | undefined;
setting: ISetting;
}
import { IApplication } from '@/operators';
import { ISetting, IState, IToken } from './models';

const store = createStore({
state(): IState {
return {
accessToken: undefined,
token: {
access: undefined,
refresh: undefined
},
setting: {
endpoint: ENDPOINT,
stream: false
}
},
applications: []
};
},
mutations: {
setAccessToken(state: IState, payload: string): void {
state.accessToken = payload;
setToken(state: IState, payload: IToken): void {
state.token = {
...state.token,
...payload
};
},
setSetting(state: IState, payload: ISetting): void {
state.setting = {
...state.setting,
...payload
};
},
setApplications(state: IState, payload: IApplication[]): void {
state.applications = payload;
}
},
actions: {
resetAuth({ commit }) {
commit('setAccessToken', undefined);
resetToken({ commit }) {
commit('setToken', {});
removeCookies();
},
setAccessToken({ commit }: ActionContext<IState, IState>, payload: string) {
commit('setAccessToken', payload);
setToken({ commit }: ActionContext<IState, IState>, payload: IToken) {
commit('setToken', payload);
},
setSetting({ commit }: ActionContext<IState, IState>, payload: ISetting) {
commit('setSetting', payload);
}
},
getters: {
authenticated(state): boolean {
return !!state.accessToken;
return !!state.token.access;
},
accessToken(state): string | undefined {
return state.accessToken;
token(state): IToken {
return state.token;
},
setting(state): ISetting | undefined {
return state.setting;
},
applications(state): IApplication[] | undefined {
return state.applications;
}
},
plugins: [createPersistedState()]
Expand Down
Loading

0 comments on commit 1d6b00c

Please sign in to comment.