Skip to content

Commit

Permalink
feat: add auto complete
Browse files Browse the repository at this point in the history
  • Loading branch information
xingwanying committed Dec 12, 2024
1 parent 4dccb75 commit f96230f
Show file tree
Hide file tree
Showing 12 changed files with 178 additions and 93 deletions.
4 changes: 2 additions & 2 deletions client/.kiwi/en/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ export default {
chongXinShengChengPei: 'Regenerate Configuration',
ziDongShengChengPei: 'Automatically Generate Configuration',
diZhiYouWu: 'Invalid Address',
qingShuRuGI: 'Please enter the GitHub project URL',
qingShuRuGI: 'Please enter or select the GitHub project name',
fuZhiTOK: 'Copy Token',
tOKEN: 'Token has been copied to clipboard',
gITHU: 'GitHub Project URL',
gITHU: 'GitHub project name',
bangWoPeiZhiYi: 'Help me create a Q&A bot',
chuCiJianMianXian:
'👋🏻 Hello! Nice to meet you. Let me introduce myself: I am PeterCat, a robot for an open-source project. You can create a Q&A robot by talking to me.',
Expand Down
4 changes: 2 additions & 2 deletions client/.kiwi/ja/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ export default {
chongXinShengChengPei: '設定を再生成',
ziDongShengChengPei: '設定を自動生成',
diZhiYouWu: 'アドレスに誤りがあります',
qingShuRuGI: 'GitHubプロジェクトのアドレスを入力してください',
qingShuRuGI: 'GitHubプロジェクト名を入力または選択してください',
fuZhiTOK: 'トークンをコピー',
tOKEN: 'トークンがクリップボードにコピーされました',
gITHU: 'GitHubプロジェクトのアドレス',
gITHU: 'GitHubプロジェクト名',
bangWoPeiZhiYi: 'Q&Aボットの設定を手伝ってください',
chuCiJianMianXian:
'👋🏻 こんにちは!初めまして、自己紹介させていただきます。私はPeterCatと申します。オープンソースプロジェクトのロボットです。私と対話することで、Q&Aロボットを設定できます。',
Expand Down
4 changes: 2 additions & 2 deletions client/.kiwi/ko/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ export default {
chongXinShengChengPei: '구성 다시 생성',
ziDongShengChengPei: '구성 자동 생성',
diZhiYouWu: '주소 오류',
qingShuRuGI: 'GitHub 프로젝트 주소를 입력하십시오.',
qingShuRuGI: 'GitHub 프로젝트 이름을 입력하거나 선택하세요',
fuZhiTOK: '토큰 복사',
tOKEN: '토큰이 클립보드에 복사되었습니다.',
gITHU: 'GitHub 프로젝트 주소',
gITHU: 'GitHub 프로젝트 이름',
bangWoPeiZhiYi: 'Q&A 봇 설정을 도와주세요.',
chuCiJianMianXian:
'👋🏻 안녕하세요! 처음 뵙겠습니다. 제 소개를 하겠습니다: 저는 PeterCat입니다, 오픈소스 프로젝트의 로봇입니다. 저와 대화를 통해 질의응답 로봇을 구성할 수 있습니다.',
Expand Down
4 changes: 2 additions & 2 deletions client/.kiwi/zh-CN/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ export default {
chongXinShengChengPei: '重新生成配置',
ziDongShengChengPei: '自动生成配置',
diZhiYouWu: '地址有误',
qingShuRuGI: '请输入 GitHub 项目地址',
qingShuRuGI: '请输入或选择 GitHub 项目名称',
fuZhiTOK: '复制 Token',
tOKEN: 'Token 已复制到剪贴板',
gITHU: 'Github 项目地址',
gITHU: 'Github 项目名称',
bangWoPeiZhiYi: '帮我配置一个答疑机器人',
chuCiJianMianXian:
'👋🏻 初次见面,先自我介绍一下:我是 PeterCat,一个开源项目的机器人。你可以通过和我对话配置一个答疑机器人。',
Expand Down
4 changes: 2 additions & 2 deletions client/.kiwi/zh-TW/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ export default {
chongXinShengChengPei: '重新生成配置',
ziDongShengChengPei: '自動生成配置',
diZhiYouWu: '地址有誤',
qingShuRuGI: '請輸入 GitHub 項目地址',
qingShuRuGI: '請輸入或選擇 GitHub 項目名稱',
fuZhiTOK: '複製 Token',
tOKEN: 'Token 已複製到剪貼板',
gITHU: 'GitHub 項目地址',
gITHU: 'GitHub 項目名稱',
bangWoPeiZhiYi: '幫我配置一個答疑機器人',
chuCiJianMianXian:
'👋🏻 初次見面,先自我介紹一下:我是 PeterCat,一個開源項目的機器人。你可以通過和我對話配置一個答疑機器人。',
Expand Down
111 changes: 67 additions & 44 deletions client/app/factory/edit/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';
import I18N from '@/app/utils/I18N';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState, Key } from 'react';
import {
Tabs,
Tab,
Expand All @@ -10,9 +10,11 @@ import {
ModalBody,
ModalFooter,
Button,
Input,
Avatar,
Checkbox,
Autocomplete,
AutocompleteItem,
Input,
} from '@nextui-org/react';
import Image from 'next/image';
import BotCreateFrom from '@/app/factory/edit/components/BotCreateForm';
Expand All @@ -28,20 +30,19 @@ import {
} from '@/app/hooks/useBot';
import { useAgreement, useAgreementStatus } from '@/app/hooks/useAgreement';
import FullPageSkeleton from '@/components/FullPageSkeleton';
import { isEmpty } from 'lodash';
import { isEmpty, map, size } from 'lodash';
import { Chat } from '@petercatai/assistant';
import AIBtnIcon from '@/public/icons/AIBtnIcon';
import ChatIcon from '@/public/icons/ChatIcon';
import ConfigIcon from '@/public/icons/ConfigIcon';
import SaveIcon from '@/public/icons/SaveIcon';
import { useBot } from '@/app/contexts/BotContext';
import useUser from '@/app/hooks/useUser';
import { useUser, useUserRepos } from '@/app/hooks/useUser';
import Knowledge from './components/Knowledge';
import { useGlobal } from '@/app/contexts/GlobalContext';
import KnowledgeBtn from './components/KnowledgeBtn';
import { BotTaskProvider } from './components/TaskContext';
import { useSearchParams } from 'next/navigation';
import 'react-toastify/dist/ReactToastify.css';
import { extractFullRepoNameFromGitHubUrl } from '@/app/utils/tools';
import DeployBotModal from './components/DeployBotModal';
import Markdown from '@/components/Markdown';
Expand All @@ -51,6 +52,8 @@ import AgreementJA from '../../../.kiwi/ja/agreement.md';
import AgreementKO from '../../../.kiwi/ko/agreement.md';
import AgreementZhTW from '../../../.kiwi/zh-TW/agreement.md';

import 'react-toastify/dist/ReactToastify.css';

const API_HOST = process.env.NEXT_PUBLIC_API_DOMAIN;
enum VisibleTypeEnum {
BOT_CONFIG = 'BOT_CONFIG',
Expand Down Expand Up @@ -78,7 +81,7 @@ export default function Edit() {
const [visibleType, setVisibleType] = React.useState<VisibleTypeEnum>(
VisibleTypeEnum.BOT_CONFIG,
);
const [gitUrl, setGitUrl] = React.useState<string>('');
const [gitRepoName, setGitRepoName] = React.useState<string>('');
const [deployModalIsOpen, setDeployModalIsOpen] = useState(false);
const [agreementModalIsOpen, setAgreementModalIsOpen] = useState(false);
const [agreementAccepted, setAgreementAccepted] =
Expand Down Expand Up @@ -180,6 +183,8 @@ export default function Edit() {
[id, botProfile?.id],
);

const { data: repos } = useUserRepos(!isEdit);

const botId = useMemo(() => {
if (!!id && id !== 'new') {
return id;
Expand Down Expand Up @@ -323,46 +328,64 @@ export default function Edit() {
)}
</div>
);
const manualConfigLabel = (
<div className="flex justify-between">
<span>{I18N.edit.page.gITHU}</span>
{botProfile.id && (
<CopyToClipboard
text={botProfile.id}
onCopy={() => {
toast.success(I18N.edit.page.tOKEN);
}}
>
{/* @ts-ignore */}
<span className="text-xs text-gray-500 cursor-pointer">
{I18N.edit.page.fuZhiTOK}
</span>
</CopyToClipboard>
)}
</div>
);

const manualConfigContent = (
<div className="h-full px-10 py-10 overflow-x-hidden overflow-y-scroll">
<div className="px-[46px]">
<Input
type="text"
variant="bordered"
name="repo_name"
label={manualConfigLabel}
disabled={isEdit}
placeholder={I18N.edit.page.qingShuRuGI}
labelPlacement="outside"
onChange={(e) => {
const url = e.target.value;
setGitUrl(url);
}}
value={gitUrl || botProfile.repoName}
isDisabled={isEdit}
required
classNames={{ label: 'w-full' }}
className="mt-1 mb-6 block w-full border-gray-300 rounded-md shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
/>
<div className="flex justify-between">
<span>{I18N.edit.page.gITHU}</span>
{botProfile.id && (
<CopyToClipboard
text={botProfile.id}
onCopy={() => {
toast.success(I18N.edit.page.tOKEN);
}}
>
{/* @ts-ignore */}
<span className="text-xs text-gray-500 cursor-pointer">
{I18N.edit.page.fuZhiTOK}
</span>
</CopyToClipboard>
)}
</div>
<div>
{!isEdit ? (
<Autocomplete
name="repo_name"
isRequired
defaultItems={repos}
allowsEmptyCollection
onInputChange={(value: string) => {
const repoName = extractFullRepoNameFromGitHubUrl(value);
setGitRepoName(repoName || '');
}}
onSelectionChange={(key) => {
setGitRepoName(`${key}`);
}}
allowsCustomValue
defaultInputValue={gitRepoName || botProfile.repoName}
variant="bordered"
className="mt-1 mb-6 block w-full border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
label={I18N.edit.page.qingShuRuGI}
>
{map(repos, (item) => (
<AutocompleteItem key={item.key}>{item.label}</AutocompleteItem>
))}
</Autocomplete>
) : (
<Input
type="text"
variant="bordered"
name="repo_name"
placeholder={I18N.edit.page.qingShuRuGI}
labelPlacement="outside"
value={gitRepoName || botProfile.repoName}
isDisabled={isEdit}
required
classNames={{ label: 'w-full' }}
className="mt-1 mb-6 block w-full border-gray-300 rounded-md shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
/>
)}

<div className="flex items-center gap-4">
{!isEdit ? (
<div className="w-full text-center">
Expand All @@ -372,7 +395,7 @@ export default function Edit() {
startContent={<AIBtnIcon />}
isLoading={createBotLoading}
onClick={() => {
const repoName = extractFullRepoNameFromGitHubUrl(gitUrl);
const repoName = gitRepoName || botProfile.repoName;
if (repoName) {
onCreateBot({
repo_name: repoName!!,
Expand Down
22 changes: 18 additions & 4 deletions client/app/hooks/useUser.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,33 @@
import { useUser as useAssistUser } from '@petercatai/assistant';
import { useFingerprint } from './useFingerprint';
import { useQuery } from '@tanstack/react-query';
import { getUserRepos } from '../services/UserController';
import { map } from 'lodash';

const API_DOMAIN = process.env.NEXT_PUBLIC_API_DOMAIN!;

export default function useUser() {
export const useUser = () => {
const { data: fingerprint } = useFingerprint();
const { user, isLoading, actions } = useAssistUser({
apiDomain: API_DOMAIN,
fingerprint: fingerprint?.visitorId!
fingerprint: fingerprint?.visitorId!,
});

return {
user,
isLoading,
actions,
status: isLoading ? "pending" : 'success',
status: isLoading ? 'pending' : 'success',
};
}
};

export const useUserRepos = (enabled: boolean) => {
return useQuery({
queryKey: [`user.repos`],
queryFn: async () => getUserRepos(),
enabled,
select: (data) =>
map(data.data, (item) => ({ label: item.name, key: item.name })),
retry: true,
});
};
7 changes: 7 additions & 0 deletions client/app/services/UserController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ export async function getAgreementStatus() {
return response.data;
}

export async function getUserRepos() {
const response = await axios.get(`${apiDomain}/api/auth/repos`, {
withCredentials: true,
});
return response.data;
}

export async function getAvailableLLMs() {
const response = await axios.get(`${apiDomain}/api/user/llms`, {
withCredentials: true,
Expand Down
9 changes: 5 additions & 4 deletions client/app/utils/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,19 @@ export const extractParametersByTools = (content: string) => {
return null;
};

export const extractFullRepoNameFromGitHubUrl = (githubUrl: string) => {
export const extractFullRepoNameFromGitHubUrl = (input: string) => {
try {
const regex = /^https:\/\/github\.com\/([^\/]+)\/([^\/]+)(\/.*)?$/;
const match = githubUrl.match(regex);
// Use a regex that matches both full URLs and `username/reponame` format.
const regex = /^(?:https:\/\/github\.com\/)?([^\/]+)\/([^\/]+)(?:\/.*)?$/;
const match = input.match(regex);

if (match && match[1] && match[2]) {
return `${match[1]}/${match[2]}`;
} else {
return null;
}
} catch (error) {
console.error('Error parsing GitHub URL:', error);
console.error('Error parsing input:', error);
return null;
}
};
9 changes: 5 additions & 4 deletions client/components/User.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
DropdownMenu,
DropdownTrigger,
} from '@nextui-org/react';
import useUser from '../app/hooks/useUser';
import { useUser } from '../app/hooks/useUser';
import GitHubIcon from '@/public/icons/GitHubIcon';
import Link from 'next/link';

Expand All @@ -23,13 +23,14 @@ export default function Profile() {
className="min-w-[88px] px-4 h-10 inline-block transition-colors bg-[#3F3F46] text-[#FFFFFF] rounded-full leading-10 text-center"
>
<GitHubIcon className="inline scale-75 -translate-y-0.5" />
{I18N.components.User.dengLu}</Button>
{I18N.components.User.dengLu}
</Button>
);
}

const avatar = (
<Dropdown className="cursor-pointer">
<DropdownTrigger >
<DropdownTrigger>
<Avatar
src={user.picture!}
alt={user.name!}
Expand All @@ -42,7 +43,7 @@ export default function Profile() {
<DropdownItem>
<Link href="/user/tokens">{I18N.components.User.tOKEN}</Link>
</DropdownItem>
<DropdownItem onClick={actions.doLogout}>
<DropdownItem onClick={actions.doLogout}>
{I18N.components.User.dengChu}
</DropdownItem>
</DropdownMenu>
Expand Down
1 change: 1 addition & 0 deletions server/auth/get_user_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

AUTH0_DOMAIN = get_env_variable("AUTH0_DOMAIN")


async def getUserInfoByToken(token):
userinfo_url = f"https://{AUTH0_DOMAIN}/userinfo"
headers = {"authorization": f"Bearer {token}"}
Expand Down
Loading

0 comments on commit f96230f

Please sign in to comment.