diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c715a90..f3be1aa 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,15 +16,22 @@ jobs: pull-requests: write # to be able to comment on released pull requests id-token: write # to enable use of OIDC for npm provenance steps: + - name: ci-install token + id: cicd + uses: getsentry/action-github-app-token@v2 + with: + app_id: ${{ secrets.APP_ID }} + private_key: ${{ secrets.APP_PRIVATE_KEY }} - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 + persist-credentials: false - name: Semantic Release id: semantic_release uses: cycjimmy/semantic-release-action@v4 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ steps.cicd.outputs.token }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Set default (old) release version @@ -43,6 +50,12 @@ jobs: needs: release runs-on: ubuntu-latest steps: + - name: ci-install token + id: cicd + uses: getsentry/action-github-app-token@v2 + with: + app_id: ${{ secrets.APP_ID }} + private_key: ${{ secrets.APP_PRIVATE_KEY }} - uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 @@ -83,6 +96,7 @@ jobs: - name: Update yaml uses: fjogeleit/yaml-update-action@main with: + token: ${{ steps.cicd.outputs.token }} valueFile: 'k8s/deployment-dev.yaml' propertyPath: 'spec.template.spec.containers[0].image' value: ghcr.io/fhswf/openai-ui:${{ env.DOCKER_METADATA_OUTPUT_VERSION }} diff --git a/CHANGELOG.md b/CHANGELOG.md index ec657bb..aa34912 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,32 @@ +## [0.5.4](https://github.com/fhswf/openai-ui/compare/v0.5.3...v0.5.4) (2024-03-20) + + +### Bug Fixes + +* favicon ([e14c8e3](https://github.com/fhswf/openai-ui/commit/e14c8e3d7a46f39f04269d16047348ecef3dae04)) +* markdown highlighting ([5b4c8ac](https://github.com/fhswf/openai-ui/commit/5b4c8ac052f124c345cc6441705dea428c640c85)) + +## [0.5.3](https://github.com/fhswf/openai-ui/compare/v0.5.2...v0.5.3) (2024-03-20) + + +### Bug Fixes + +* workflow permissions ([b76fc30](https://github.com/fhswf/openai-ui/commit/b76fc30efffd265f28e2bd98a23d27713aa24147)) + +## [0.5.2](https://github.com/fhswf/openai-ui/compare/v0.5.1...v0.5.2) (2024-03-20) + + +### Bug Fixes + +* cookies for cors ([1c24d60](https://github.com/fhswf/openai-ui/commit/1c24d60729378f455ea999306d91685fe7c57920)) + +## [0.5.1](https://github.com/fhswf/openai-ui/compare/v0.5.0...v0.5.1) (2024-03-19) + + +### Bug Fixes + +* option passing to openai service ([fd486b1](https://github.com/fhswf/openai-ui/commit/fd486b19245f5c21a17d5212d70b89ca8f6cf333)) + # [0.5.0](https://github.com/fhswf/openai-ui/compare/v0.4.2...v0.5.0) (2024-03-19) diff --git a/index.html b/index.html index 486a58f..2a44377 100644 --- a/index.html +++ b/index.html @@ -3,9 +3,9 @@ - + - GPT-CHAT + K!mpuls, der FH Chatbot diff --git a/k8s/deployment-dev.yaml b/k8s/deployment-dev.yaml index 4fc5efb..93ca463 100644 --- a/k8s/deployment-dev.yaml +++ b/k8s/deployment-dev.yaml @@ -19,7 +19,7 @@ spec: io.kompose.service: openai-ui spec: containers: - - image: ghcr.io/fhswf/openai-ui:sha-9cda321 + - image: ghcr.io/fhswf/openai-ui:sha-e612fb4 name: ui ports: - containerPort: 80 diff --git a/package-lock.json b/package-lock.json index 0c440e7..2148558 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "openai-ui", - "version": "0.5.0", + "version": "0.5.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "openai-ui", - "version": "0.5.0", + "version": "0.5.4", "license": "ISC", "dependencies": { "eventsource-parser": "^1.0.0", diff --git a/package.json b/package.json index dbc5202..856b304 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openai-ui", - "version": "0.5.0", + "version": "0.5.4", "description": "Privacy-focused UI for OpenAI APIs", "main": "index.js", "keywords": [], @@ -9,7 +9,7 @@ "type": "module", "private": true, "scripts": { - "start": "vite", + "start": "NODE_ENV='production' vite", "build": "vite build" }, "dependencies": { diff --git a/public/favicon.ico b/public/favicon.ico deleted file mode 100644 index aec0bf9..0000000 Binary files a/public/favicon.ico and /dev/null differ diff --git a/public/favicon.png b/public/favicon.png new file mode 100644 index 0000000..ed2fb1b Binary files /dev/null and b/public/favicon.png differ diff --git a/src/assets/style/common.less b/src/assets/style/common.less index de66469..b1049f4 100644 --- a/src/assets/style/common.less +++ b/src/assets/style/common.less @@ -67,15 +67,19 @@ --text-color: #dfe2eb; --text-color-gray: #a7acbb; } + [data-size="small"] { --font-size-base: 12px; } + [data-size="middle"] { --font-size-base: 13px; } + [data-size="default"] { --font-size-base: 14px; } + [data-size="large"] { --font-size-base: 16px; } @@ -83,11 +87,10 @@ html { background-color: var(--background-color-gray); } + * { vertical-align: baseline; - font-weight: inherit; - font-family: inherit; - font-style: inherit; + font-size: 100%; outline: 0; padding: 0; @@ -102,35 +105,43 @@ textarea { color: var(--text-color); display: block; width: 100%; + &::placeholder { color: var(--text-color-gray); } } + body { font-family: sans-serif; color: var(--text-color); } + .flex { display: flex; + &-c { display: flex; align-items: center; + &-c { display: flex; align-items: center; justify-content: center; } + &-sb { display: flex; align-items: center; justify-content: space-between; } } + &-column { display: flex; flex-direction: column; } + &-1 { flex: 1; } -} +} \ No newline at end of file diff --git a/src/chat/MessageRender.jsx b/src/chat/MessageRender.jsx index 4b3dbe7..a6e9333 100644 --- a/src/chat/MessageRender.jsx +++ b/src/chat/MessageRender.jsx @@ -1,5 +1,5 @@ import React, { memo } from 'react' -import ReactMarkdown from 'react-markdown' +import Markdown from 'react-markdown' import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' import { oneLight, oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism' import { useGlobal } from './context' @@ -14,7 +14,7 @@ export const MessageRender = memo((props) => { const { options } = useGlobal() const style = options.general.theme === 'dark' ? oneDark : oneLight return ( - ) => void; - clearTypeing: () => void; - sendMessage: () => void; - setApp: (app: any) => void; - newChat: (app: any) => void; - modifyChat: (arg: any, index: number) => void; - editChat: (index: number, title: string) => void; - removeChat: (index: number) => void; - setMessage: (content: string) => void; - clearMessage: () => void; - removeMessage: (id: number) => void; - setOptions: (arg: OptionAction) => void; - setIs: (arg: any) => void; - currentList: () => any; - stopResonse: () => void; -}; - -export default function action(state, dispatch): GlobalAction { - const setState = (payload = {}) => +import { AccountOptions, GeneralOptions, GlobalState, OpenAIOptions, Options, OptionAction, GlobalActions, Messages, GlobalAction, GlobalActionType, AnyOptions, OptionActionType } from "./types"; +import React from "react"; + + +export default function action(state: Partial, dispatch: React.Dispatch): GlobalActions { + const setState = (payload: Partial = {}) => dispatch({ - type: "SET_STATE", + type: GlobalActionType.SET_STATE, payload: { ...payload }, }); return { @@ -53,13 +37,13 @@ export default function action(state, dispatch): GlobalAction { }, async newChat(app) { - const { _currentApp, is, options, currentChat, chat } = state; - const currentApp = app || _currentApp; - let messages = [{ content: currentApp?.content || t("system_welcome"), sentTime: Date.now(), role: "system", id: 1, }] - console.log("newChat: ", currentApp, chat) + const { currentApp, is, options, currentChat, chat } = state; + const newApp = app || currentApp; + let messages = [{ content: newApp?.content || t("system_welcome"), sentTime: Date.now(), role: "system", id: 1, }] + console.log("newChat: ", newApp, chat) const chatList = [ { - title: currentApp?.title || t("new_conversation"), + title: newApp?.title || t("new_conversation"), id: Date.now(), messages, ct: Date.now(), @@ -70,7 +54,7 @@ export default function action(state, dispatch): GlobalAction { let _chat = chatList; setState({ chat: _chat, currentChat: 0 }); console.log("newChat: ", _chat) - if (currentApp.botStarts) { + if (newApp.botStarts) { console.log("botStarts"); await executeChatRequest(setState, is, _chat, messages, options, 0, chat); } @@ -84,7 +68,8 @@ export default function action(state, dispatch): GlobalAction { editChat(index, title) { const chat = [...state.chat]; - chat.splice(index, 1, [...chat[index], title]); + const _chat = { ...chat[index], title }; + chat.splice(index, 1, _chat); setState({ chat, }); @@ -134,12 +119,15 @@ export default function action(state, dispatch): GlobalAction { setOptions({ type, data }: OptionAction) { console.log('set options: ', type, data); let options = { ...state.options }; + if (type === OptionActionType.OPENAI) { + } options[type] = { ...options[type], ...data }; if (type === "general") { if (data.language) { i18next.changeLanguage(data.language); } } + console.log('set options: ', options); setState({ options }); }, @@ -160,7 +148,7 @@ export default function action(state, dispatch): GlobalAction { }; } -async function executeChatRequest(setState, is, newChat, messages, options: Options, currentChat, chat) { +async function executeChatRequest(setState, is, newChat, messages: Messages, options: Options, currentChat, chat) { setState({ is: { ...is, thinking: true }, typeingMessage: {}, diff --git a/src/chat/context/index.tsx b/src/chat/context/index.tsx index d10126a..54d46ad 100644 --- a/src/chat/context/index.tsx +++ b/src/chat/context/index.tsx @@ -10,81 +10,7 @@ import action from "./action"; import reducer from "./reducer"; import { initState } from "./initState"; import { fetchAndGetUser } from "../utils"; - - -export enum GlobalActionType { - SET_STATE = "SET_STATE", - CHANGE_MESSAGE = "CHANGE_MESSAGE", - IS_CONFIG = "IS_CONFIG", -}; - -export enum OptionActionType { - GENERAL = "general", - ACCOUNT = "account", - OPENAI = "openai" -} - -export type GeneralOptions = { - language: string; - theme: string; - sendCommand: string; - size: string; -}; - -export type AccountOptions = { - name: string; - avatar: string; -}; - -export type OpenAIOptions = { - baseUrl: string; - organizationId: string; - temperature: number; - top_p: number; - model: string; - apiKey: string; - max_tokens: number; - n: number; - stream: boolean; -}; - -export type Options = { - account: AccountOptions; - general: GeneralOptions; - openai: OpenAIOptions; -}; - -export type GlobalState = { - conversation: any[]; - current: number; - chat: any[]; - currentChat: number; - options: Options; - - is: { - typeing: boolean; - config: boolean; - fullScreen: boolean; - sidebar: boolean; - inputing: boolean; - thinking: boolean; - apps: boolean; - }; - typeingMessage: any; - user: any; - version: string; - content: string; -}; - -export type GlobalAction = - | { type: GlobalActionType.SET_STATE; payload: Partial } - | { type: GlobalActionType.CHANGE_MESSAGE; payload: Partial } - | { type: GlobalActionType.IS_CONFIG; payload: Partial }; - -export type OptionAction = - | { type: OptionActionType.GENERAL; data: Partial; } - | { type: OptionActionType.ACCOUNT; data: Partial; } - | { type: OptionActionType.OPENAI; data: Partial; }; +import { GlobalAction, GlobalState, GlobalActionType } from "./types"; export const ChatContext = createContext(null); diff --git a/src/chat/context/initState.ts b/src/chat/context/initState.ts index 12832d5..8c4d5f0 100644 --- a/src/chat/context/initState.ts +++ b/src/chat/context/initState.ts @@ -1,10 +1,9 @@ -import { GlobalState } from "."; +import { GlobalState } from "./types"; import i18n from "../../i18n/config"; const { t } = i18n; export const initState: GlobalState = { - conversation: [], current: 0, chat: [ { @@ -23,6 +22,7 @@ export const initState: GlobalState = { ], currentChat: 0, + currentApp: null, options: { account: { name: "Anonymus", diff --git a/src/chat/context/reducer.ts b/src/chat/context/reducer.ts index 1282a8c..9ba12b5 100644 --- a/src/chat/context/reducer.ts +++ b/src/chat/context/reducer.ts @@ -1,4 +1,4 @@ -import { GlobalState, GlobalAction } from "."; +import { GlobalState, GlobalAction } from "./types"; // TODO: refactor this to use proper actions and types export default function reduce(state: GlobalState, action: GlobalAction): GlobalState { diff --git a/src/chat/context/types.ts b/src/chat/context/types.ts new file mode 100644 index 0000000..1754753 --- /dev/null +++ b/src/chat/context/types.ts @@ -0,0 +1,119 @@ + + +export enum GlobalActionType { + SET_STATE = "SET_STATE", + CHANGE_MESSAGE = "CHANGE_MESSAGE", + IS_CONFIG = "IS_CONFIG" +} +; + +export enum OptionActionType { + GENERAL = "general", + ACCOUNT = "account", + OPENAI = "openai" +} + +export type GeneralOptions = { + language: string; + theme: string; + sendCommand: string; + size: string; +}; + +export type AccountOptions = { + name: string; + avatar: string; +}; + +export type OpenAIOptions = { + baseUrl: string; + organizationId: string; + temperature: number; + top_p: number; + model: string; + apiKey: string; + max_tokens: number; + n: number; + stream: boolean; +}; + +export type Options = { + account: AccountOptions; + general: GeneralOptions; + openai: OpenAIOptions; +}; + +export type AnyOptions = GeneralOptions | AccountOptions | OpenAIOptions; + +export type GlobalState = { + current: number; + chat: Chat[]; + currentChat: number; + currentApp: App | null; + options: Options; + is: { + typeing: boolean; + config: boolean; + fullScreen: boolean; + sidebar: boolean; + inputing: boolean; + thinking: boolean; + apps: boolean; + }; + typeingMessage: any; + user: any; + version: string; + content: string; +}; + +export type GlobalAction = { type: GlobalActionType.SET_STATE; payload: Partial; } | +{ type: GlobalActionType.CHANGE_MESSAGE; payload: Partial; } | +{ type: GlobalActionType.IS_CONFIG; payload: Partial; }; + +export type OptionAction = { type: OptionActionType.GENERAL; data: Partial; } | +{ type: OptionActionType.ACCOUNT; data: Partial; } | +{ type: OptionActionType.OPENAI; data: Partial; }; + + +export type GlobalActions = { + setState: (payload: Partial) => void; + clearTypeing: () => void; + sendMessage: () => void; + setApp: (app: any) => void; + newChat: (app: any) => void; + modifyChat: (arg: any, index: number) => void; + editChat: (index: number, title: string) => void; + removeChat: (index: number) => void; + setMessage: (content: string) => void; + clearMessage: () => void; + removeMessage: (id: number) => void; + setOptions: (arg: OptionAction) => void; + setIs: (arg: any) => void; + currentList: () => any; + stopResonse: () => void; +}; + +export type Message = { + content: string; + sentTime: number; + role: string; + id: number; +}; + +export type Messages = Message[]; + +export type Chat = { + title: string; + id: number; + ct: string; + messages: Messages; +}; + +export type App = { + category: number; + title: string; + desc: string; + id: number; + content: string; + botStarts: boolean; +}; \ No newline at end of file diff --git a/src/chat/service/openai.ts b/src/chat/service/openai.ts index 6f103fe..f4f4679 100644 --- a/src/chat/service/openai.ts +++ b/src/chat/service/openai.ts @@ -1,6 +1,6 @@ import { createParser } from "eventsource-parser"; import { setAbortController } from "./abortController.mjs"; -import { Options, OpenAIOptions } from "../context"; +import { Options, OpenAIOptions } from "../context/types"; export async function* streamAsyncIterable(stream) { const reader = stream.getReader(); @@ -67,6 +67,7 @@ export const fetchAction = async (method: string, headers, body, signal, + credentials: "include" }); return response; }; diff --git a/src/chat/utils/index.js b/src/chat/utils/index.js index c0f9db9..3ee7920 100644 --- a/src/chat/utils/index.js +++ b/src/chat/utils/index.js @@ -26,7 +26,7 @@ export async function sha256Digest(message) { export function fetchAndGetUser(dispatch) { - fetch(import.meta.env.VITE_USER_URL) + fetch(import.meta.env.VITE_USER_URL, { credentials: "include" }) .catch(err => { console.log("error getting user: ", err); })