generated from template/astro-template
update add mark
This commit is contained in:
parent
e38b8df9f0
commit
22cb48b9f3
@ -36,6 +36,7 @@
|
|||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.1.1",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
|
"fuse.js": "^7.1.0",
|
||||||
"highlight.js": "^11.11.1",
|
"highlight.js": "^11.11.1",
|
||||||
"i18next": "^25.2.0",
|
"i18next": "^25.2.0",
|
||||||
"i18next-browser-languagedetector": "^8.1.0",
|
"i18next-browser-languagedetector": "^8.1.0",
|
||||||
|
9
pnpm-lock.yaml
generated
9
pnpm-lock.yaml
generated
@ -65,6 +65,9 @@ importers:
|
|||||||
dayjs:
|
dayjs:
|
||||||
specifier: ^1.11.13
|
specifier: ^1.11.13
|
||||||
version: 1.11.13
|
version: 1.11.13
|
||||||
|
fuse.js:
|
||||||
|
specifier: ^7.1.0
|
||||||
|
version: 7.1.0
|
||||||
highlight.js:
|
highlight.js:
|
||||||
specifier: ^11.11.1
|
specifier: ^11.11.1
|
||||||
version: 11.11.1
|
version: 11.11.1
|
||||||
@ -2069,6 +2072,10 @@ packages:
|
|||||||
function-bind@1.1.2:
|
function-bind@1.1.2:
|
||||||
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
||||||
|
|
||||||
|
fuse.js@7.1.0:
|
||||||
|
resolution: {integrity: sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
gensync@1.0.0-beta.2:
|
gensync@1.0.0-beta.2:
|
||||||
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
@ -5342,6 +5349,8 @@ snapshots:
|
|||||||
|
|
||||||
function-bind@1.1.2: {}
|
function-bind@1.1.2: {}
|
||||||
|
|
||||||
|
fuse.js@7.1.0: {}
|
||||||
|
|
||||||
gensync@1.0.0-beta.2: {}
|
gensync@1.0.0-beta.2: {}
|
||||||
|
|
||||||
get-east-asian-width@1.3.0: {}
|
get-east-asian-width@1.3.0: {}
|
||||||
|
@ -1,153 +0,0 @@
|
|||||||
import { query } from '@/modules/query';
|
|
||||||
import { Query } from '@kevisual/query';
|
|
||||||
import { DataOpts } from '@kevisual/query/query';
|
|
||||||
|
|
||||||
export class QueryChat {
|
|
||||||
query: Query;
|
|
||||||
|
|
||||||
constructor({ query }: { query: Query }) {
|
|
||||||
this.query = query;
|
|
||||||
}
|
|
||||||
|
|
||||||
getChatList(opts?: DataOpts) {
|
|
||||||
return this.query.post(
|
|
||||||
{
|
|
||||||
path: 'ai',
|
|
||||||
key: 'get-chat-list',
|
|
||||||
},
|
|
||||||
opts,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getChat(id: string, opts?: DataOpts) {
|
|
||||||
return this.query.post(
|
|
||||||
{
|
|
||||||
path: 'ai',
|
|
||||||
key: 'get-chat',
|
|
||||||
data: {
|
|
||||||
id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
opts,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
updateChat(data: any, opts?: DataOpts) {
|
|
||||||
return this.query.post(
|
|
||||||
{
|
|
||||||
path: 'ai',
|
|
||||||
key: 'update-chat',
|
|
||||||
data,
|
|
||||||
},
|
|
||||||
opts,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteChat(id: string, opts?: DataOpts) {
|
|
||||||
return this.query.post(
|
|
||||||
{
|
|
||||||
path: 'ai',
|
|
||||||
key: 'delete-chat',
|
|
||||||
data: {
|
|
||||||
id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
opts,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 获取模型列表
|
|
||||||
* @param opts
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
getModelList(data?: { usernames?: string[] }, opts?: DataOpts) {
|
|
||||||
return this.query.post(
|
|
||||||
{
|
|
||||||
path: 'ai',
|
|
||||||
key: 'get-model-list',
|
|
||||||
data,
|
|
||||||
},
|
|
||||||
opts,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 聊天对话模型
|
|
||||||
* @param data
|
|
||||||
* @param chatOpts
|
|
||||||
* @param opts
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
chat(data: ChatDataOpts, chatOpts: ChatOpts, opts?: DataOpts) {
|
|
||||||
const { username, model, group, getFull = true } = chatOpts;
|
|
||||||
if (!username || !model || !group) {
|
|
||||||
throw new Error('username, model, group is required');
|
|
||||||
}
|
|
||||||
return this.query.post(
|
|
||||||
{
|
|
||||||
path: 'ai',
|
|
||||||
key: 'chat',
|
|
||||||
...chatOpts,
|
|
||||||
getFull,
|
|
||||||
data,
|
|
||||||
},
|
|
||||||
opts,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
clearConfigCache(opts?: DataOpts) {
|
|
||||||
return this.query.post(
|
|
||||||
{
|
|
||||||
path: 'ai',
|
|
||||||
key: 'clear-cache',
|
|
||||||
},
|
|
||||||
opts,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 获取聊天使用情况
|
|
||||||
* @param opts
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
getChatUsage(opts?: DataOpts) {
|
|
||||||
return this.query.post(
|
|
||||||
{
|
|
||||||
path: 'ai',
|
|
||||||
key: 'get-chat-usage',
|
|
||||||
},
|
|
||||||
opts,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 清除当前用户模型自己的统计
|
|
||||||
* @param opts
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
clearSelfUsage(opts?: DataOpts) {
|
|
||||||
return this.query.post(
|
|
||||||
{
|
|
||||||
path: 'ai',
|
|
||||||
key: 'clear-chat-limit',
|
|
||||||
},
|
|
||||||
opts,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export type ChatDataOpts = {
|
|
||||||
id?: string;
|
|
||||||
title?: string;
|
|
||||||
messages?: any[];
|
|
||||||
data?: any;
|
|
||||||
type?: 'temp' | 'keep' | string;
|
|
||||||
};
|
|
||||||
export type ChatOpts = {
|
|
||||||
username: string;
|
|
||||||
model: string;
|
|
||||||
/**
|
|
||||||
* 获取完整消息回复
|
|
||||||
*/
|
|
||||||
getFull?: boolean;
|
|
||||||
group: string;
|
|
||||||
/**
|
|
||||||
* openai的参数
|
|
||||||
*/
|
|
||||||
options?: any;
|
|
||||||
};
|
|
@ -5,11 +5,11 @@ import { query } from '@/modules/query';
|
|||||||
import { useStore, BoundStore } from '@kevisual/store/react';
|
import { useStore, BoundStore } from '@kevisual/store/react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { ChastHistoryMessage } from './type';
|
import { ChastHistoryMessage } from './type';
|
||||||
import { QueryChat } from '../query/chat';
|
|
||||||
import { CacheStore } from '@kevisual/cache/cache-store';
|
import { CacheStore } from '@kevisual/cache/cache-store';
|
||||||
import { QueryMark } from '@/query/query-mark/query-mark';
|
import { QueryMark } from '@/query/query-mark/query-mark';
|
||||||
|
import { QueryApp as QueryAi } from '@/query/query-ai/query-ai';
|
||||||
export const queryMark = new QueryMark({ query: query, markType: 'chat' });
|
export const queryMark = new QueryMark({ query: query, markType: 'chat' });
|
||||||
export const queryChat = new QueryChat({ query });
|
export const queryChat = new QueryAi({ query });
|
||||||
|
|
||||||
const dbName = 'chat';
|
const dbName = 'chat';
|
||||||
export const store = useContextKey('store', () => {
|
export const store = useContextKey('store', () => {
|
||||||
|
112
src/apps/ai-mark/index.tsx
Normal file
112
src/apps/ai-mark/index.tsx
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import { query } from '@/modules/query';
|
||||||
|
import { QueryMark, Mark } from '@/query/query-mark/query-mark';
|
||||||
|
import { useEffect, useRef, useState, useCallback } from 'react';
|
||||||
|
import { Tooltip } from '@/components/a/tooltip';
|
||||||
|
export const queryMark = new QueryMark({ query: query, markType: 'md' });
|
||||||
|
import Fuse from 'fuse.js';
|
||||||
|
import { debounce } from 'lodash-es';
|
||||||
|
import { SquareArrowOutUpRight } from 'lucide-react';
|
||||||
|
|
||||||
|
export const App = () => {
|
||||||
|
const [list, setList] = useState<Mark[]>([]);
|
||||||
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
|
const [searchResults, setSearchResults] = useState<Mark[]>([]);
|
||||||
|
const fuseRef = useRef<Fuse<Mark>>(null);
|
||||||
|
|
||||||
|
// 实际执行搜索的函数
|
||||||
|
const performSearch = (term: string) => {
|
||||||
|
if (!term.trim()) {
|
||||||
|
setSearchResults([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fuseRef.current) {
|
||||||
|
const results = fuseRef.current.search(term);
|
||||||
|
setSearchResults(results.map((result) => result.item));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 使用 useRef 和 useCallback 创建防抖搜索函数,确保它在组件生命周期中只创建一次
|
||||||
|
const debouncedSearchRef = useRef(
|
||||||
|
debounce((term: string) => {
|
||||||
|
performSearch(term);
|
||||||
|
}, 1000), // 300毫秒的防抖延迟
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
init();
|
||||||
|
|
||||||
|
// 组件卸载时取消待执行的防抖函数
|
||||||
|
return () => {
|
||||||
|
debouncedSearchRef.current.cancel();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const init = async () => {
|
||||||
|
const res = await queryMark.getMarkList();
|
||||||
|
if (res.code === 200) {
|
||||||
|
const list = res.data?.list || [];
|
||||||
|
setList(list!);
|
||||||
|
const fuse = new Fuse(list, {
|
||||||
|
keys: ['title', 'description', 'tags', 'summary'],
|
||||||
|
});
|
||||||
|
fuseRef.current = fuse;
|
||||||
|
const result = fuse.search('苏生不惑的博客');
|
||||||
|
console.log(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSearch = (term: string) => {
|
||||||
|
setSearchTerm(term); // 立即更新输入框的值
|
||||||
|
debouncedSearchRef.current(term); // 使用防抖函数进行搜索
|
||||||
|
};
|
||||||
|
|
||||||
|
const onOpen = (item: Mark) => {
|
||||||
|
const link = item.link;
|
||||||
|
window.open(link);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 确定要显示的列表:有搜索内容时显示搜索结果,否则显示全部
|
||||||
|
const displayList = searchTerm.trim() ? searchResults : list;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='w-full h-full overflow-auto'>
|
||||||
|
<div className='sticky top-0 p-4 bg-white z-10 shadow-sm'>
|
||||||
|
<input
|
||||||
|
type='text'
|
||||||
|
value={searchTerm}
|
||||||
|
onChange={(e) => handleSearch(e.target.value)}
|
||||||
|
placeholder='搜索书签...'
|
||||||
|
className='w-full p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-300'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='p-4'>
|
||||||
|
{displayList.length === 0 && searchTerm.trim() ? (
|
||||||
|
<div className='text-center text-gray-500 my-8'>没有找到匹配的结果</div>
|
||||||
|
) : (
|
||||||
|
displayList.map((item) => {
|
||||||
|
return (
|
||||||
|
<div key={item.id} className='p-4 mb-3 border rounded-md hover:shadow-md transition-shadow' title={item.link}>
|
||||||
|
<Tooltip title={item.link}>
|
||||||
|
<h3 className='text-lg font-semibold mb-1 flex gap-2 items-center'>
|
||||||
|
{item.title || '-'}
|
||||||
|
|
||||||
|
<SquareArrowOutUpRight className='h-5 w-5 cursor-pointer' onClick={() => onOpen(item)} />
|
||||||
|
</h3>
|
||||||
|
</Tooltip>
|
||||||
|
<p className='text-sm text-gray-600 mb-2'>{item.summary}</p>
|
||||||
|
<div className='flex flex-wrap gap-1'>
|
||||||
|
{item.tags.map((tag, index) => (
|
||||||
|
<span key={index} className='px-2 py-1 bg-blue-100 text-blue-800 text-xs rounded-md'>
|
||||||
|
{tag}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
33
src/apps/assistant-home/index.tsx
Normal file
33
src/apps/assistant-home/index.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { Button } from '@/components/a/button.tsx';
|
||||||
|
import { TextEditor } from '@kevisual/markdown-editor/tiptap/editor.ts';
|
||||||
|
import { Select } from '@/components/a/select.tsx';
|
||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { getSuggestionItems } from '../ai-chat/editor/suggestion/item';
|
||||||
|
import { html2md } from '@kevisual/markdown-editor/tiptap/index.ts';
|
||||||
|
import { chatId } from '../ai-chat/utils/uuid';
|
||||||
|
import '../ai-chat/index.css';
|
||||||
|
export const App = (props: any) => {
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
const editorRef = useRef<TextEditor | null>(null);
|
||||||
|
useEffect(() => {
|
||||||
|
if (ref.current) {
|
||||||
|
editorRef.current = new TextEditor();
|
||||||
|
editorRef.current.createEditor(ref.current, {
|
||||||
|
items: getSuggestionItems(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
if (ref.current && editorRef.current) {
|
||||||
|
editorRef.current?.destroy?.();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
return (
|
||||||
|
<div className='w-full h-full flex flex-col'>
|
||||||
|
<div className='w-[600px] h-[400px] border border-gray-300 flex flex-col mx-auto'>
|
||||||
|
<div className='w-full scrollbar' style={{ height: 'calc(100% - 90px)' }} ref={ref}></div>
|
||||||
|
</div>
|
||||||
|
<Button>发送</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
11
src/data/blogs/logger-life/0003-ai-token.md
Normal file
11
src/data/blogs/logger-life/0003-ai-token.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
share: private
|
||||||
|
title: "AI Token 记录"
|
||||||
|
date: 2025-05-25 01:30:00
|
||||||
|
---
|
||||||
|
|
||||||
|
当使用 AI 的时候,如果需要做什么任务的话,其实感觉 token 的消耗数是非常的大的。如果想要消耗的不那么大,就必须要精致化的功能模块,代码就必须要细致。
|
||||||
|
|
||||||
|
比如指令和任务匹配,比如文本需要抽取。
|
||||||
|
|
||||||
|
为什么我会觉得消耗大呢,假如用户有一个 agent 任务,首先要分析用户的意图,要把所有要调用的函数都列出来给到ai,当ai返回结果的时候,还要把结果返回给用户,用户调用对应的函数,生成的结果又给到ai,ai再返回给用户。在n多次的循环中,就有很多的消耗。
|
@ -19,3 +19,8 @@ export const randomId = (number: number) => {
|
|||||||
const _letter = uuid(1);
|
const _letter = uuid(1);
|
||||||
return `${_letter}${nanoid(number)}`;
|
return `${_letter}${nanoid(number)}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const randomLetter = (number: number = 8, opts?: { before?: string; after?: string }) => {
|
||||||
|
const { before = '', after = '' } = opts || {};
|
||||||
|
return `${before}${uuid(number)}${after}`;
|
||||||
|
};
|
||||||
|
@ -1,5 +1,30 @@
|
|||||||
|
import { toast } from 'react-toastify';
|
||||||
import { Query, ClientQuery } from '@kevisual/query/query';
|
import { Query, ClientQuery } from '@kevisual/query/query';
|
||||||
|
import { QueryLoginBrowser } from '@/query/query-login/query-login-browser';
|
||||||
|
import { toastLogin } from './toast/ToastLogin';
|
||||||
export const query = new Query();
|
export const query = new Query();
|
||||||
|
|
||||||
export const clientQuery = new ClientQuery();
|
export const clientQuery = new ClientQuery();
|
||||||
|
|
||||||
|
export const queryLogin = new QueryLoginBrowser({
|
||||||
|
query: query as any,
|
||||||
|
});
|
||||||
|
|
||||||
|
query.beforeRequest = queryLogin.beforeRequest.bind(queryLogin);
|
||||||
|
query.afterResponse = async (res, ctx) => {
|
||||||
|
const newRes = await queryLogin.run401Action(res, ctx, {
|
||||||
|
afterAlso401: () => {
|
||||||
|
toastLogin();
|
||||||
|
},
|
||||||
|
afterCheck: (res) => {
|
||||||
|
console.log('afterCheck', res);
|
||||||
|
if (res.code === 200) {
|
||||||
|
toast.success('刷新登陆信息');
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return newRes as any;
|
||||||
|
};
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
import '../styles/global.css';
|
import '@/styles/global.css';
|
||||||
import Blank from '@/components/html/blank.astro';
|
import Blank from '@/components/html/blank.astro';
|
||||||
import { App as AIChat } from '@/apps/ai-chat/index.tsx';
|
import { App as AssistantHome } from '@/apps/assistant-home/index.tsx';
|
||||||
---
|
---
|
||||||
|
|
||||||
<Blank>
|
<Blank>
|
||||||
<AIChat client:only />
|
<AssistantHome client:only />
|
||||||
</Blank>
|
</Blank>
|
||||||
|
9
src/pages/mark/ai-chat.astro
Normal file
9
src/pages/mark/ai-chat.astro
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
import '@/styles/global.css';
|
||||||
|
import Blank from '@/components/html/blank.astro';
|
||||||
|
import { App as AIChat } from '@/apps/ai-chat/index.tsx';
|
||||||
|
---
|
||||||
|
|
||||||
|
<Blank>
|
||||||
|
<AIChat client:only />
|
||||||
|
</Blank>
|
9
src/pages/mark/ai-mark.astro
Normal file
9
src/pages/mark/ai-mark.astro
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
import '@/styles/global.css';
|
||||||
|
import Blank from '@/components/html/blank.astro';
|
||||||
|
import { App } from '@/apps/ai-mark';
|
||||||
|
---
|
||||||
|
|
||||||
|
<Blank>
|
||||||
|
<App client:only />
|
||||||
|
</Blank>
|
14
src/pages/test/2025/01-get-query.astro
Normal file
14
src/pages/test/2025/01-get-query.astro
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
import '@/styles/global.css';
|
||||||
|
import Blank from '@/components/html/blank.astro';
|
||||||
|
import { QueryMark } from '@/query/query-mark/query-mark';
|
||||||
|
---
|
||||||
|
|
||||||
|
<Blank >
|
||||||
|
<div>
|
||||||
|
<h1>Get Query</h1>
|
||||||
|
<script type='module'>
|
||||||
|
console.log('load');
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
</Blank>
|
@ -12,6 +12,27 @@ export type PostChat = {
|
|||||||
user?: string;
|
user?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ChatDataOpts = {
|
||||||
|
id?: string;
|
||||||
|
title?: string;
|
||||||
|
messages?: any[];
|
||||||
|
data?: any;
|
||||||
|
type?: 'temp' | 'keep' | string;
|
||||||
|
};
|
||||||
|
export type ChatOpts = {
|
||||||
|
username: string;
|
||||||
|
model: string;
|
||||||
|
/**
|
||||||
|
* 获取完整消息回复
|
||||||
|
*/
|
||||||
|
getFull?: boolean;
|
||||||
|
group: string;
|
||||||
|
/**
|
||||||
|
* openai的参数
|
||||||
|
*/
|
||||||
|
options?: any;
|
||||||
|
};
|
||||||
|
|
||||||
export const appDefine = QueryUtil.create({
|
export const appDefine = QueryUtil.create({
|
||||||
chat: {
|
chat: {
|
||||||
path: 'ai',
|
path: 'ai',
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { appDefine } from './defines/ai.ts';
|
import { appDefine } from './defines/ai.ts';
|
||||||
import { PostChat } from './defines/ai.ts';
|
import { PostChat, ChatOpts, ChatDataOpts } from './defines/ai.ts';
|
||||||
|
|
||||||
import { BaseQuery, DataOpts, Query } from '@kevisual/query/query';
|
import { BaseQuery, DataOpts, Query } from '@kevisual/query/query';
|
||||||
|
|
||||||
@ -22,4 +22,80 @@ export class QueryApp<T extends Query = Query> extends BaseQuery<T, typeof appDe
|
|||||||
postChat(data: PostChat, opts?: DataOpts) {
|
postChat(data: PostChat, opts?: DataOpts) {
|
||||||
return this.chain('chat').post(data, opts);
|
return this.chain('chat').post(data, opts);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 获取模型列表
|
||||||
|
* @param opts
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getModelList(data?: { usernames?: string[] }, opts?: DataOpts) {
|
||||||
|
return this.query.post(
|
||||||
|
{
|
||||||
|
path: 'ai',
|
||||||
|
key: 'get-model-list',
|
||||||
|
data,
|
||||||
|
},
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 聊天对话模型
|
||||||
|
* @param data
|
||||||
|
* @param chatOpts
|
||||||
|
* @param opts
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
chat(data: ChatDataOpts, chatOpts: ChatOpts, opts?: DataOpts) {
|
||||||
|
const { username, model, group, getFull = true } = chatOpts;
|
||||||
|
if (!username || !model || !group) {
|
||||||
|
throw new Error('username, model, group is required');
|
||||||
|
}
|
||||||
|
return this.query.post(
|
||||||
|
{
|
||||||
|
path: 'ai',
|
||||||
|
key: 'chat',
|
||||||
|
...chatOpts,
|
||||||
|
getFull,
|
||||||
|
data,
|
||||||
|
},
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
clearConfigCache(opts?: DataOpts) {
|
||||||
|
return this.query.post(
|
||||||
|
{
|
||||||
|
path: 'ai',
|
||||||
|
key: 'clear-cache',
|
||||||
|
},
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取聊天使用情况
|
||||||
|
* @param opts
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getChatUsage(opts?: DataOpts) {
|
||||||
|
return this.query.post(
|
||||||
|
{
|
||||||
|
path: 'ai',
|
||||||
|
key: 'get-chat-usage',
|
||||||
|
},
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除当前用户模型自己的统计
|
||||||
|
* @param opts
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
clearSelfUsage(opts?: DataOpts) {
|
||||||
|
return this.query.post(
|
||||||
|
{
|
||||||
|
path: 'ai',
|
||||||
|
key: 'clear-chat-limit',
|
||||||
|
},
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ export class QueryLogin extends BaseQuery {
|
|||||||
super({
|
super({
|
||||||
query: opts?.query || new Query(),
|
query: opts?.query || new Query(),
|
||||||
});
|
});
|
||||||
this.cacheStore = new LoginCacheStore({ name: 'login', cache: opts.cache });
|
this.cacheStore = new LoginCacheStore({ name: 'login', cache: opts?.cache! });
|
||||||
this.isBrowser = opts?.isBrowser ?? true;
|
this.isBrowser = opts?.isBrowser ?? true;
|
||||||
this.init();
|
this.init();
|
||||||
this.onLoad = opts?.onLoad;
|
this.onLoad = opts?.onLoad;
|
||||||
@ -271,7 +271,7 @@ export class QueryLogin extends BaseQuery {
|
|||||||
config.headers['Authorization'] = `Bearer ${_token}`;
|
config.headers['Authorization'] = `Bearer ${_token}`;
|
||||||
}
|
}
|
||||||
if (!_token) {
|
if (!_token) {
|
||||||
// TODO: 取消请求,因为没有登陆
|
return false;
|
||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
@ -303,6 +303,21 @@ export class QueryLogin extends BaseQuery {
|
|||||||
const token = this.storage.getItem('token');
|
const token = this.storage.getItem('token');
|
||||||
return !!token;
|
return !!token;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 检查本地用户列表
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async getToken() {
|
||||||
|
const token = this.storage.getItem('token');
|
||||||
|
return token || '';
|
||||||
|
}
|
||||||
|
async beforeRequest(opts: any = {}) {
|
||||||
|
const token = this.storage.getItem('token');
|
||||||
|
if (token) {
|
||||||
|
opts.headers = { ...opts.headers, Authorization: `Bearer ${token}` };
|
||||||
|
}
|
||||||
|
return opts;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 请求更新,切换用户, 使用switchUser
|
* 请求更新,切换用户, 使用switchUser
|
||||||
* @param username
|
* @param username
|
||||||
@ -318,7 +333,7 @@ export class QueryLogin extends BaseQuery {
|
|||||||
*/
|
*/
|
||||||
async switchUser(username: string) {
|
async switchUser(username: string) {
|
||||||
const localUserList = await this.cacheStore.getCurrentUserList();
|
const localUserList = await this.cacheStore.getCurrentUserList();
|
||||||
const user = localUserList.find((userItem) => userItem.user.username === username);
|
const user = localUserList.find((userItem) => userItem.user!.username === username);
|
||||||
if (user) {
|
if (user) {
|
||||||
this.storage.setItem('token', user.accessToken || '');
|
this.storage.setItem('token', user.accessToken || '');
|
||||||
await this.beforeSetLoginUser({ accessToken: user.accessToken, refreshToken: user.refreshToken });
|
await this.beforeSetLoginUser({ accessToken: user.accessToken, refreshToken: user.refreshToken });
|
||||||
|
@ -143,7 +143,7 @@ export class QueryMark extends QueryMarkBase<SimpleObject> {
|
|||||||
this.markType = opts?.markType || 'simple';
|
this.markType = opts?.markType || 'simple';
|
||||||
}
|
}
|
||||||
async getMarkList(search?: SearchOpts, opts?: DataOpts) {
|
async getMarkList(search?: SearchOpts, opts?: DataOpts) {
|
||||||
return this.post({ key: 'list', ...search, markType: this.markType }, opts);
|
return this.post<Result<ResultMarkList>>({ key: 'list', ...search, markType: this.markType }, opts);
|
||||||
}
|
}
|
||||||
async updateMark(data: any, opts?: DataOpts) {
|
async updateMark(data: any, opts?: DataOpts) {
|
||||||
if (!data.id) {
|
if (!data.id) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user