mirror of
https://github.com/ItzCrazyKns/Perplexica.git
synced 2026-01-07 11:58:24 +00:00
Compare commits
23 Commits
feat/impro
...
v1.12.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b8d8be676 | ||
|
|
b83f9bac78 | ||
|
|
bd7c563137 | ||
|
|
23b903db9a | ||
|
|
a98f0df83f | ||
|
|
164d528761 | ||
|
|
af4ec17117 | ||
|
|
1622e0893a | ||
|
|
55a4b9d436 | ||
|
|
b450d0e668 | ||
|
|
0987ee4370 | ||
|
|
d1bd22786d | ||
|
|
bb7b7170ca | ||
|
|
be7bd62a74 | ||
|
|
a691f3bab0 | ||
|
|
f1c9fa0e33 | ||
|
|
d872cf5009 | ||
|
|
fdef718980 | ||
|
|
19dde42f22 | ||
|
|
c9f6893d99 | ||
|
|
53e9859b6c | ||
|
|
53e39cd985 | ||
|
|
fdaa2f0646 |
@@ -239,8 +239,8 @@ Perplexica runs on Next.js and handles all API requests. It works right away on
|
||||
|
||||
## Upcoming Features
|
||||
|
||||
- [] Adding more widgets, integrations, search sources
|
||||
- [] Adding authentication
|
||||
- [ ] Adding more widgets, integrations, search sources
|
||||
- [ ] Adding authentication
|
||||
|
||||
## Support Us
|
||||
|
||||
|
||||
@@ -7,11 +7,8 @@ services:
|
||||
- '3000:3000'
|
||||
volumes:
|
||||
- data:/home/perplexica/data
|
||||
- uploads:/home/perplexica/uploads
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
data:
|
||||
name: 'perplexica-data'
|
||||
uploads:
|
||||
name: 'perplexica-uploads'
|
||||
name: 'perplexica-data'
|
||||
2
next-env.d.ts
vendored
2
next-env.d.ts
vendored
@@ -1,6 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/dev/types/routes.d.ts";
|
||||
import './.next/dev/types/routes.d.ts';
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
|
||||
@@ -11,6 +11,13 @@ const nextConfig = {
|
||||
],
|
||||
},
|
||||
serverExternalPackages: ['pdf-parse'],
|
||||
outputFileTracingIncludes: {
|
||||
'/api/**': [
|
||||
'./node_modules/@napi-rs/canvas/**',
|
||||
'./node_modules/@napi-rs/canvas-linux-x64-gnu/**',
|
||||
'./node_modules/@napi-rs/canvas-linux-x64-musl/**',
|
||||
],
|
||||
},
|
||||
env: {
|
||||
NEXT_PUBLIC_VERSION: pkg.version,
|
||||
},
|
||||
|
||||
14
package.json
14
package.json
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "perplexica-frontend",
|
||||
"version": "1.11.2",
|
||||
"name": "perplexica",
|
||||
"version": "1.12.1",
|
||||
"license": "MIT",
|
||||
"author": "ItzCrazyKns",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"dev": "next dev --webpack",
|
||||
"build": "next build --webpack",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"format:write": "prettier . --write"
|
||||
@@ -19,7 +19,7 @@
|
||||
"@phosphor-icons/react": "^2.1.10",
|
||||
"@radix-ui/react-tooltip": "^1.2.8",
|
||||
"@tailwindcss/typography": "^0.5.12",
|
||||
"@types/jspdf": "^2.0.0",
|
||||
"@toolsycc/json-repair": "^0.1.22",
|
||||
"axios": "^1.8.3",
|
||||
"better-sqlite3": "^11.9.1",
|
||||
"clsx": "^2.1.0",
|
||||
@@ -54,6 +54,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/better-sqlite3": "^7.6.12",
|
||||
"@types/jspdf": "^2.0.0",
|
||||
"@types/node": "^24.8.1",
|
||||
"@types/pdf-parse": "^1.1.4",
|
||||
"@types/react": "^18",
|
||||
@@ -68,5 +69,8 @@
|
||||
"prettier": "^3.2.5",
|
||||
"tailwindcss": "^3.3.0",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@napi-rs/canvas": "^0.1.87"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,10 @@ export const POST = async (req: Request) => {
|
||||
|
||||
const images = await searchImages(
|
||||
{
|
||||
chatHistory: body.chatHistory,
|
||||
chatHistory: body.chatHistory.map(([role, content]) => ({
|
||||
role: role === 'human' ? 'user' : 'assistant',
|
||||
content,
|
||||
})),
|
||||
query: body.query,
|
||||
},
|
||||
llm,
|
||||
|
||||
@@ -20,7 +20,10 @@ export const POST = async (req: Request) => {
|
||||
|
||||
const suggestions = await generateSuggestions(
|
||||
{
|
||||
chatHistory: body.chatHistory,
|
||||
chatHistory: body.chatHistory.map(([role, content]) => ({
|
||||
role: role === 'human' ? 'user' : 'assistant',
|
||||
content,
|
||||
})),
|
||||
},
|
||||
llm,
|
||||
);
|
||||
|
||||
@@ -21,7 +21,10 @@ export const POST = async (req: Request) => {
|
||||
|
||||
const videos = await handleVideoSearch(
|
||||
{
|
||||
chatHistory: body.chatHistory,
|
||||
chatHistory: body.chatHistory.map(([role, content]) => ({
|
||||
role: role === 'human' ? 'user' : 'assistant',
|
||||
content,
|
||||
})),
|
||||
query: body.query,
|
||||
},
|
||||
llm,
|
||||
|
||||
@@ -80,7 +80,10 @@ const Chat = () => {
|
||||
{loading && !messageAppeared && <MessageBoxLoading />}
|
||||
<div ref={messageEnd} className="h-0" />
|
||||
{dividerWidth > 0 && (
|
||||
<div className="fixed z-40 bottom-24 lg:bottom-6" style={{ width: dividerWidth }}>
|
||||
<div
|
||||
className="fixed z-40 bottom-24 lg:bottom-6"
|
||||
style={{ width: dividerWidth }}
|
||||
>
|
||||
<div
|
||||
className="pointer-events-none absolute -bottom-6 left-0 right-0 h-[calc(100%+24px+24px)] dark:hidden"
|
||||
style={{
|
||||
|
||||
@@ -50,7 +50,14 @@ const MessageBox = ({
|
||||
dividerRef?: MutableRefObject<HTMLDivElement | null>;
|
||||
isLast: boolean;
|
||||
}) => {
|
||||
const { loading, sendMessage, rewrite, messages, researchEnded } = useChat();
|
||||
const {
|
||||
loading,
|
||||
sendMessage,
|
||||
rewrite,
|
||||
messages,
|
||||
researchEnded,
|
||||
chatHistory,
|
||||
} = useChat();
|
||||
|
||||
const parsedMessage = section.parsedTextBlocks.join('\n\n');
|
||||
const speechMessage = section.speechMessage || '';
|
||||
@@ -265,11 +272,11 @@ const MessageBox = ({
|
||||
<div className="lg:sticky lg:top-20 flex flex-col items-center space-y-3 w-full lg:w-3/12 z-30 h-full pb-4">
|
||||
<SearchImages
|
||||
query={section.message.query}
|
||||
chatHistory={messages}
|
||||
chatHistory={chatHistory}
|
||||
messageId={section.message.messageId}
|
||||
/>
|
||||
<SearchVideos
|
||||
chatHistory={messages}
|
||||
chatHistory={chatHistory}
|
||||
query={section.message.query}
|
||||
messageId={section.message.messageId}
|
||||
/>
|
||||
|
||||
@@ -17,7 +17,7 @@ const SearchImages = ({
|
||||
messageId,
|
||||
}: {
|
||||
query: string;
|
||||
chatHistory: Message[];
|
||||
chatHistory: [string, string][];
|
||||
messageId: string;
|
||||
}) => {
|
||||
const [images, setImages] = useState<Image[] | null>(null);
|
||||
|
||||
@@ -30,7 +30,7 @@ const Searchvideos = ({
|
||||
messageId,
|
||||
}: {
|
||||
query: string;
|
||||
chatHistory: Message[];
|
||||
chatHistory: [string, string][];
|
||||
messageId: string;
|
||||
}) => {
|
||||
const [videos, setVideos] = useState<Video[] | null>(null);
|
||||
|
||||
@@ -175,7 +175,7 @@ const loadMessages = async (
|
||||
chatId: string,
|
||||
setMessages: (messages: Message[]) => void,
|
||||
setIsMessagesLoaded: (loaded: boolean) => void,
|
||||
setChatHistory: (history: [string, string][]) => void,
|
||||
chatHistory: React.MutableRefObject<[string, string][]>,
|
||||
setSources: (sources: string[]) => void,
|
||||
setNotFound: (notFound: boolean) => void,
|
||||
setFiles: (files: File[]) => void,
|
||||
@@ -233,7 +233,7 @@ const loadMessages = async (
|
||||
setFiles(files);
|
||||
setFileIds(files.map((file: File) => file.fileId));
|
||||
|
||||
setChatHistory(history);
|
||||
chatHistory.current = history;
|
||||
setSources(data.chat.sources);
|
||||
setIsMessagesLoaded(true);
|
||||
};
|
||||
@@ -281,7 +281,7 @@ export const ChatProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
|
||||
const [researchEnded, setResearchEnded] = useState(false);
|
||||
|
||||
const [chatHistory, setChatHistory] = useState<[string, string][]>([]);
|
||||
const chatHistory = useRef<[string, string][]>([]);
|
||||
const [messages, setMessages] = useState<Message[]>([]);
|
||||
|
||||
const [files, setFiles] = useState<File[]>([]);
|
||||
@@ -402,7 +402,12 @@ export const ChatProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
});
|
||||
}, [messages]);
|
||||
|
||||
const isReconnectingRef = useRef(false);
|
||||
const handledMessageEndRef = useRef<Set<string>>(new Set());
|
||||
|
||||
const checkReconnect = async () => {
|
||||
if (isReconnectingRef.current) return;
|
||||
|
||||
setIsReady(true);
|
||||
console.debug(new Date(), 'app:ready');
|
||||
|
||||
@@ -414,6 +419,8 @@ export const ChatProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
setResearchEnded(false);
|
||||
setMessageAppeared(false);
|
||||
|
||||
isReconnectingRef.current = true;
|
||||
|
||||
const res = await fetch(`/api/reconnect/${lastMsg.backendId}`, {
|
||||
method: 'POST',
|
||||
});
|
||||
@@ -427,23 +434,27 @@ export const ChatProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
|
||||
const messageHandler = getMessageHandler(lastMsg);
|
||||
|
||||
while (true) {
|
||||
const { value, done } = await reader.read();
|
||||
if (done) break;
|
||||
try {
|
||||
while (true) {
|
||||
const { value, done } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
partialChunk += decoder.decode(value, { stream: true });
|
||||
partialChunk += decoder.decode(value, { stream: true });
|
||||
|
||||
try {
|
||||
const messages = partialChunk.split('\n');
|
||||
for (const msg of messages) {
|
||||
if (!msg.trim()) continue;
|
||||
const json = JSON.parse(msg);
|
||||
messageHandler(json);
|
||||
try {
|
||||
const messages = partialChunk.split('\n');
|
||||
for (const msg of messages) {
|
||||
if (!msg.trim()) continue;
|
||||
const json = JSON.parse(msg);
|
||||
messageHandler(json);
|
||||
}
|
||||
partialChunk = '';
|
||||
} catch (error) {
|
||||
console.warn('Incomplete JSON, waiting for next chunk...');
|
||||
}
|
||||
partialChunk = '';
|
||||
} catch (error) {
|
||||
console.warn('Incomplete JSON, waiting for next chunk...');
|
||||
}
|
||||
} finally {
|
||||
isReconnectingRef.current = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -463,7 +474,7 @@ export const ChatProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
if (params.chatId && params.chatId !== chatId) {
|
||||
setChatId(params.chatId);
|
||||
setMessages([]);
|
||||
setChatHistory([]);
|
||||
chatHistory.current = [];
|
||||
setFiles([]);
|
||||
setFileIds([]);
|
||||
setIsMessagesLoaded(false);
|
||||
@@ -483,7 +494,7 @@ export const ChatProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
chatId,
|
||||
setMessages,
|
||||
setIsMessagesLoaded,
|
||||
setChatHistory,
|
||||
chatHistory,
|
||||
setSources,
|
||||
setNotFound,
|
||||
setFiles,
|
||||
@@ -519,9 +530,7 @@ export const ChatProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
|
||||
setMessages((prev) => prev.slice(0, index));
|
||||
|
||||
setChatHistory((prev) => {
|
||||
return prev.slice(0, index * 2);
|
||||
});
|
||||
chatHistory.current = chatHistory.current.slice(0, index * 2);
|
||||
|
||||
const messageToRewrite = messages[index];
|
||||
sendMessage(messageToRewrite.query, messageToRewrite.messageId, true);
|
||||
@@ -570,6 +579,20 @@ export const ChatProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
setMessages((prev) =>
|
||||
prev.map((msg) => {
|
||||
if (msg.messageId === messageId) {
|
||||
const exists = msg.responseBlocks.findIndex(
|
||||
(b) => b.id === data.block.id,
|
||||
);
|
||||
|
||||
if (exists !== -1) {
|
||||
const existingBlocks = [...msg.responseBlocks];
|
||||
existingBlocks[exists] = data.block;
|
||||
|
||||
return {
|
||||
...msg,
|
||||
responseBlocks: existingBlocks,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...msg,
|
||||
responseBlocks: [...msg.responseBlocks, data.block],
|
||||
@@ -607,12 +630,18 @@ export const ChatProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
}
|
||||
|
||||
if (data.type === 'messageEnd') {
|
||||
if (handledMessageEndRef.current.has(messageId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
handledMessageEndRef.current.add(messageId);
|
||||
|
||||
const currentMsg = messagesRef.current.find(
|
||||
(msg) => msg.messageId === messageId,
|
||||
);
|
||||
|
||||
const newHistory: [string, string][] = [
|
||||
...chatHistory,
|
||||
...chatHistory.current,
|
||||
['human', message.query],
|
||||
[
|
||||
'assistant',
|
||||
@@ -621,7 +650,7 @@ export const ChatProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
],
|
||||
];
|
||||
|
||||
setChatHistory(newHistory);
|
||||
chatHistory.current = newHistory;
|
||||
|
||||
setMessages((prev) =>
|
||||
prev.map((msg) =>
|
||||
@@ -638,13 +667,15 @@ export const ChatProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const autoMediaSearch = getAutoMediaSearch();
|
||||
|
||||
if (autoMediaSearch) {
|
||||
document
|
||||
.getElementById(`search-images-${lastMsg.messageId}`)
|
||||
?.click();
|
||||
setTimeout(() => {
|
||||
document
|
||||
.getElementById(`search-images-${lastMsg.messageId}`)
|
||||
?.click();
|
||||
|
||||
document
|
||||
.getElementById(`search-videos-${lastMsg.messageId}`)
|
||||
?.click();
|
||||
document
|
||||
.getElementById(`search-videos-${lastMsg.messageId}`)
|
||||
?.click();
|
||||
}, 200);
|
||||
}
|
||||
|
||||
// Check if there are sources and no suggestions
|
||||
@@ -728,8 +759,11 @@ export const ChatProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
sources: sources,
|
||||
optimizationMode: optimizationMode,
|
||||
history: rewrite
|
||||
? chatHistory.slice(0, messageIndex === -1 ? undefined : messageIndex)
|
||||
: chatHistory,
|
||||
? chatHistory.current.slice(
|
||||
0,
|
||||
messageIndex === -1 ? undefined : messageIndex,
|
||||
)
|
||||
: chatHistory.current,
|
||||
chatModel: {
|
||||
key: chatModelProvider.key,
|
||||
providerId: chatModelProvider.providerId,
|
||||
@@ -776,7 +810,7 @@ export const ChatProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
value={{
|
||||
messages,
|
||||
sections,
|
||||
chatHistory,
|
||||
chatHistory: chatHistory.current,
|
||||
files,
|
||||
fileIds,
|
||||
sources,
|
||||
|
||||
@@ -7,6 +7,7 @@ import TransformersProvider from './transformers';
|
||||
import GroqProvider from './groq';
|
||||
import LemonadeProvider from './lemonade';
|
||||
import AnthropicProvider from './anthropic';
|
||||
import LMStudioProvider from './lmstudio';
|
||||
|
||||
export const providers: Record<string, ProviderConstructor<any>> = {
|
||||
openai: OpenAIProvider,
|
||||
@@ -16,6 +17,7 @@ export const providers: Record<string, ProviderConstructor<any>> = {
|
||||
groq: GroqProvider,
|
||||
lemonade: LemonadeProvider,
|
||||
anthropic: AnthropicProvider,
|
||||
lmstudio: LMStudioProvider,
|
||||
};
|
||||
|
||||
export const getModelProvidersUIConfigSection =
|
||||
|
||||
143
src/lib/models/providers/lmstudio/index.ts
Normal file
143
src/lib/models/providers/lmstudio/index.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
import { UIConfigField } from '@/lib/config/types';
|
||||
import { getConfiguredModelProviderById } from '@/lib/config/serverRegistry';
|
||||
import BaseModelProvider from '../../base/provider';
|
||||
import { Model, ModelList, ProviderMetadata } from '../../types';
|
||||
import LMStudioLLM from './lmstudioLLM';
|
||||
import BaseLLM from '../../base/llm';
|
||||
import BaseEmbedding from '../../base/embedding';
|
||||
import LMStudioEmbedding from './lmstudioEmbedding';
|
||||
|
||||
interface LMStudioConfig {
|
||||
baseURL: string;
|
||||
}
|
||||
|
||||
const providerConfigFields: UIConfigField[] = [
|
||||
{
|
||||
type: 'string',
|
||||
name: 'Base URL',
|
||||
key: 'baseURL',
|
||||
description: 'The base URL for LM Studio server',
|
||||
required: true,
|
||||
placeholder: 'http://localhost:1234',
|
||||
env: 'LM_STUDIO_BASE_URL',
|
||||
scope: 'server',
|
||||
},
|
||||
];
|
||||
|
||||
class LMStudioProvider extends BaseModelProvider<LMStudioConfig> {
|
||||
constructor(id: string, name: string, config: LMStudioConfig) {
|
||||
super(id, name, config);
|
||||
}
|
||||
|
||||
private normalizeBaseURL(url: string): string {
|
||||
const trimmed = url.trim().replace(/\/+$/, '');
|
||||
return trimmed.endsWith('/v1') ? trimmed : `${trimmed}/v1`;
|
||||
}
|
||||
|
||||
async getDefaultModels(): Promise<ModelList> {
|
||||
try {
|
||||
const baseURL = this.normalizeBaseURL(this.config.baseURL);
|
||||
|
||||
const res = await fetch(`${baseURL}/models`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
const models: Model[] = data.data.map((m: any) => {
|
||||
return {
|
||||
name: m.id,
|
||||
key: m.id,
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
embedding: models,
|
||||
chat: models,
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof TypeError) {
|
||||
throw new Error(
|
||||
'Error connecting to LM Studio. Please ensure the base URL is correct and the LM Studio server is running.',
|
||||
);
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async getModelList(): Promise<ModelList> {
|
||||
const defaultModels = await this.getDefaultModels();
|
||||
const configProvider = getConfiguredModelProviderById(this.id)!;
|
||||
|
||||
return {
|
||||
embedding: [
|
||||
...defaultModels.embedding,
|
||||
...configProvider.embeddingModels,
|
||||
],
|
||||
chat: [...defaultModels.chat, ...configProvider.chatModels],
|
||||
};
|
||||
}
|
||||
|
||||
async loadChatModel(key: string): Promise<BaseLLM<any>> {
|
||||
const modelList = await this.getModelList();
|
||||
|
||||
const exists = modelList.chat.find((m) => m.key === key);
|
||||
|
||||
if (!exists) {
|
||||
throw new Error(
|
||||
'Error Loading LM Studio Chat Model. Invalid Model Selected',
|
||||
);
|
||||
}
|
||||
|
||||
return new LMStudioLLM({
|
||||
apiKey: 'lm-studio',
|
||||
model: key,
|
||||
baseURL: this.normalizeBaseURL(this.config.baseURL),
|
||||
});
|
||||
}
|
||||
|
||||
async loadEmbeddingModel(key: string): Promise<BaseEmbedding<any>> {
|
||||
const modelList = await this.getModelList();
|
||||
const exists = modelList.embedding.find((m) => m.key === key);
|
||||
|
||||
if (!exists) {
|
||||
throw new Error(
|
||||
'Error Loading LM Studio Embedding Model. Invalid Model Selected.',
|
||||
);
|
||||
}
|
||||
|
||||
return new LMStudioEmbedding({
|
||||
apiKey: 'lm-studio',
|
||||
model: key,
|
||||
baseURL: this.normalizeBaseURL(this.config.baseURL),
|
||||
});
|
||||
}
|
||||
|
||||
static parseAndValidate(raw: any): LMStudioConfig {
|
||||
if (!raw || typeof raw !== 'object')
|
||||
throw new Error('Invalid config provided. Expected object');
|
||||
if (!raw.baseURL)
|
||||
throw new Error('Invalid config provided. Base URL must be provided');
|
||||
|
||||
return {
|
||||
baseURL: String(raw.baseURL),
|
||||
};
|
||||
}
|
||||
|
||||
static getProviderConfigFields(): UIConfigField[] {
|
||||
return providerConfigFields;
|
||||
}
|
||||
|
||||
static getProviderMetadata(): ProviderMetadata {
|
||||
return {
|
||||
key: 'lmstudio',
|
||||
name: 'LM Studio',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default LMStudioProvider;
|
||||
5
src/lib/models/providers/lmstudio/lmstudioEmbedding.ts
Normal file
5
src/lib/models/providers/lmstudio/lmstudioEmbedding.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import OpenAIEmbedding from '../openai/openaiEmbedding';
|
||||
|
||||
class LMStudioEmbedding extends OpenAIEmbedding {}
|
||||
|
||||
export default LMStudioEmbedding;
|
||||
5
src/lib/models/providers/lmstudio/lmstudioLLM.ts
Normal file
5
src/lib/models/providers/lmstudio/lmstudioLLM.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import OpenAILLM from '../openai/openaiLLM';
|
||||
|
||||
class LMStudioLLM extends OpenAILLM {}
|
||||
|
||||
export default LMStudioLLM;
|
||||
@@ -11,6 +11,7 @@ import { Ollama, Tool as OllamaTool, Message as OllamaMessage } from 'ollama';
|
||||
import { parse } from 'partial-json';
|
||||
import crypto from 'crypto';
|
||||
import { Message } from '@/lib/types';
|
||||
import { repairJson } from '@toolsycc/json-repair';
|
||||
|
||||
type OllamaConfig = {
|
||||
baseURL: string;
|
||||
@@ -205,7 +206,13 @@ class OllamaLLM extends BaseLLM<OllamaConfig> {
|
||||
});
|
||||
|
||||
try {
|
||||
return input.schema.parse(JSON.parse(response.message.content)) as T;
|
||||
return input.schema.parse(
|
||||
JSON.parse(
|
||||
repairJson(response.message.content, {
|
||||
extractJson: true,
|
||||
}) as string,
|
||||
),
|
||||
) as T;
|
||||
} catch (err) {
|
||||
throw new Error(`Error parsing response from Ollama: ${err}`);
|
||||
}
|
||||
|
||||
@@ -61,6 +61,22 @@ const defaultChatModels: Model[] = [
|
||||
name: 'GPT 5 Mini',
|
||||
key: 'gpt-5-mini',
|
||||
},
|
||||
{
|
||||
name: 'GPT 5 Pro',
|
||||
key: 'gpt-5-pro',
|
||||
},
|
||||
{
|
||||
name: 'GPT 5.1',
|
||||
key: 'gpt-5.1',
|
||||
},
|
||||
{
|
||||
name: 'GPT 5.2',
|
||||
key: 'gpt-5.2',
|
||||
},
|
||||
{
|
||||
name: 'GPT 5.2 Pro',
|
||||
key: 'gpt-5.2-pro',
|
||||
},
|
||||
{
|
||||
name: 'o1',
|
||||
key: 'o1',
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
ChatCompletionToolMessageParam,
|
||||
} from 'openai/resources/index.mjs';
|
||||
import { Message } from '@/lib/types';
|
||||
import { repairJson } from '@toolsycc/json-repair';
|
||||
|
||||
type OpenAIConfig = {
|
||||
apiKey: string;
|
||||
@@ -167,7 +168,7 @@ class OpenAILLM extends BaseLLM<OpenAIConfig> {
|
||||
contentChunk: chunk.choices[0].delta.content || '',
|
||||
toolCallChunk:
|
||||
toolCalls?.map((tc) => {
|
||||
if (tc.type === 'function') {
|
||||
if (!recievedToolCalls[tc.index]) {
|
||||
const call = {
|
||||
name: tc.function?.name!,
|
||||
id: tc.id!,
|
||||
@@ -213,7 +214,13 @@ class OpenAILLM extends BaseLLM<OpenAIConfig> {
|
||||
|
||||
if (response.choices && response.choices.length > 0) {
|
||||
try {
|
||||
return input.schema.parse(response.choices[0].message.parsed) as T;
|
||||
return input.schema.parse(
|
||||
JSON.parse(
|
||||
repairJson(response.choices[0].message.content!, {
|
||||
extractJson: true,
|
||||
}) as string,
|
||||
),
|
||||
) as T;
|
||||
} catch (err) {
|
||||
throw new Error(`Error parsing response from OpenAI: ${err}`);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Chunk } from '@/lib/types';
|
||||
import BaseEmbedding from '../../base/embedding';
|
||||
import { FeatureExtractionPipeline, pipeline } from '@huggingface/transformers';
|
||||
import { FeatureExtractionPipeline } from '@huggingface/transformers';
|
||||
|
||||
type TransformerConfig = {
|
||||
model: string;
|
||||
@@ -21,21 +21,19 @@ class TransformerEmbedding extends BaseEmbedding<TransformerConfig> {
|
||||
return this.embed(chunks.map((c) => c.content));
|
||||
}
|
||||
|
||||
async embed(texts: string[]): Promise<number[][]> {
|
||||
private async embed(texts: string[]) {
|
||||
if (!this.pipelinePromise) {
|
||||
this.pipelinePromise = (async () => {
|
||||
const transformers = await import('@huggingface/transformers');
|
||||
return (await transformers.pipeline(
|
||||
'feature-extraction',
|
||||
this.config.model,
|
||||
)) as unknown as FeatureExtractionPipeline;
|
||||
const { pipeline } = await import('@huggingface/transformers');
|
||||
const result = await pipeline('feature-extraction', this.config.model, {
|
||||
dtype: 'fp32',
|
||||
});
|
||||
return result as FeatureExtractionPipeline;
|
||||
})();
|
||||
}
|
||||
|
||||
const pipeline = await this.pipelinePromise;
|
||||
|
||||
const output = await pipeline(texts, { pooling: 'mean', normalize: true });
|
||||
|
||||
const pipe = await this.pipelinePromise;
|
||||
const output = await pipe(texts, { pooling: 'mean', normalize: true });
|
||||
return output.tolist() as number[][];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ChatTurnMessage } from '@/lib/types';
|
||||
export const imageSearchPrompt = `
|
||||
You will be given a conversation below and a follow up question. You need to rephrase the follow-up question so it is a standalone question that can be used by the LLM to search the web for images.
|
||||
You need to make sure the rephrased question agrees with the conversation and is relevant to the conversation.
|
||||
Make sure to make the querey standalone and not something very broad, use context from the answers in the conversation to make it specific so user can get best image search results.
|
||||
Output only the rephrased query in query key JSON format. Do not include any explanation or additional text.
|
||||
`;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ChatTurnMessage } from '@/lib/types';
|
||||
export const videoSearchPrompt = `
|
||||
You will be given a conversation below and a follow up question. You need to rephrase the follow-up question so it is a standalone question that can be used by the LLM to search Youtube for videos.
|
||||
You need to make sure the rephrased question agrees with the conversation and is relevant to the conversation.
|
||||
Make sure to make the querey standalone and not something very broad, use context from the answers in the conversation to make it specific so user can get best video search results.
|
||||
Output only the rephrased query in query key JSON format. Do not include any explanation or additional text.
|
||||
`;
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import axios from 'axios';
|
||||
import { getSearxngURL } from './config/serverRegistry';
|
||||
|
||||
interface SearxngSearchOptions {
|
||||
@@ -44,6 +43,6 @@ export const searchSearxng = async (
|
||||
|
||||
const results: SearxngSearchResult[] = data.results;
|
||||
const suggestions: string[] = data.suggestions;
|
||||
|
||||
|
||||
return { results, suggestions };
|
||||
};
|
||||
|
||||
@@ -4,8 +4,8 @@ import crypto from "crypto"
|
||||
import fs from 'fs';
|
||||
import { splitText } from "../utils/splitText";
|
||||
import { PDFParse } from 'pdf-parse';
|
||||
import { CanvasFactory } from 'pdf-parse/worker';
|
||||
import officeParser from 'officeparser'
|
||||
import { Chunk } from "../types";
|
||||
|
||||
const supportedMimeTypes = ['application/pdf', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'text/plain'] as const
|
||||
|
||||
@@ -116,7 +116,8 @@ class UploadManager {
|
||||
const pdfBuffer = fs.readFileSync(filePath);
|
||||
|
||||
const parser = new PDFParse({
|
||||
data: pdfBuffer
|
||||
data: pdfBuffer,
|
||||
CanvasFactory
|
||||
})
|
||||
|
||||
const pdfText = await parser.getText().then(res => res.text)
|
||||
|
||||
77
yarn.lock
77
yarn.lock
@@ -797,6 +797,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.84.tgz#7b476e3003be0aca08ab27962fd0d6e803939bec"
|
||||
integrity sha512-pdvuqvj3qtwVryqgpAGornJLV6Ezpk39V6wT4JCnRVGy8I3Tk1au8qOalFGrx/r0Ig87hWslysPpHBxVpBMIww==
|
||||
|
||||
"@napi-rs/canvas-android-arm64@0.1.87":
|
||||
version "0.1.87"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.87.tgz#6adce7741baa56e75dcf72076e4bf249f9bc4b8e"
|
||||
integrity sha512-uW7NxJXPvZft9fers4oBhdCsBRVe77DLQS3eXEOxndFzGKiwmjIbZpQqj4QPvrg3I0FM3UfHatz1+17P5SeCOQ==
|
||||
|
||||
"@napi-rs/canvas-darwin-arm64@0.1.80":
|
||||
version "0.1.80"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.80.tgz#638eaa2d0a2a373c7d15748743182718dcd95c4b"
|
||||
@@ -807,6 +812,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.84.tgz#0f131722f9f66316cea5f5ed7cfb9ad1290683cd"
|
||||
integrity sha512-A8IND3Hnv0R6abc6qCcCaOCujTLMmGxtucMTZ5vbQUrEN/scxi378MyTLtyWg+MRr6bwQJ6v/orqMS9datIcww==
|
||||
|
||||
"@napi-rs/canvas-darwin-arm64@0.1.87":
|
||||
version "0.1.87"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.87.tgz#54f82dc0cd032f85e770abcddbeafc855005931a"
|
||||
integrity sha512-S6YbpXwajDKLTsYftEqR+Ne1lHpeC78okI3IqctVdFexN31Taprn6mdV4CkPY/4S8eGNuReBHvXNyWbGqBZ1eQ==
|
||||
|
||||
"@napi-rs/canvas-darwin-x64@0.1.80":
|
||||
version "0.1.80"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.80.tgz#bd6bc048dbd4b02b9620d9d07117ed93e6970978"
|
||||
@@ -817,6 +827,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.84.tgz#e6ab8c534172d8a8d434fa090da8a205359d8769"
|
||||
integrity sha512-AUW45lJhYWwnA74LaNeqhvqYKK/2hNnBBBl03KRdqeCD4tKneUSrxUqIv8d22CBweOvrAASyKN3W87WO2zEr/A==
|
||||
|
||||
"@napi-rs/canvas-darwin-x64@0.1.87":
|
||||
version "0.1.87"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.87.tgz#54c2be73ce69a65e70f3d94fb879907479cc12c5"
|
||||
integrity sha512-OJLwP2WIUmRSqWTyV/NZ2TnvBzUsbNqQu6IL7oshwfxYg4BELPV279wrfQ/xZFqzr7wybfIzKaPF4du5ZdA2Cg==
|
||||
|
||||
"@napi-rs/canvas-linux-arm-gnueabihf@0.1.80":
|
||||
version "0.1.80"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.80.tgz#ce6bfbeb19d9234c42df5c384e5989aa7d734789"
|
||||
@@ -827,6 +842,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.84.tgz#5898daa3050a8ba4619c1d6cea3a3217d46c5ffd"
|
||||
integrity sha512-8zs5ZqOrdgs4FioTxSBrkl/wHZB56bJNBqaIsfPL4ZkEQCinOkrFF7xIcXiHiKp93J3wUtbIzeVrhTIaWwqk+A==
|
||||
|
||||
"@napi-rs/canvas-linux-arm-gnueabihf@0.1.87":
|
||||
version "0.1.87"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.87.tgz#1bc3c9280db381cc3893c3d13d7320666ce47ebe"
|
||||
integrity sha512-Io3tY6ogc+oyvIGK9rQlnfH4gKiS35P7W6s22x3WCrLFR0dXzZP2IBBoEFEHd6FY6FR1ky5u9cRmADaiLRdX3g==
|
||||
|
||||
"@napi-rs/canvas-linux-arm64-gnu@0.1.80":
|
||||
version "0.1.80"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.80.tgz#3b7a7832fef763826fa5fb740d5757204e52607d"
|
||||
@@ -837,6 +857,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.84.tgz#fbbde94c04278259f1f40b4c199dfd9f95c82e66"
|
||||
integrity sha512-i204vtowOglJUpbAFWU5mqsJgH0lVpNk/Ml4mQtB4Lndd86oF+Otr6Mr5KQnZHqYGhlSIKiU2SYnUbhO28zGQA==
|
||||
|
||||
"@napi-rs/canvas-linux-arm64-gnu@0.1.87":
|
||||
version "0.1.87"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.87.tgz#70af2d77d58d65559a43c55f6c37906c220af39e"
|
||||
integrity sha512-Zq7h/PQzs37gaSR/gNRZOAaCC1kGt6NmDjA1PcqpONITh/rAfAwAeP98emrbBJ4FDoPkYRkxmxHlmXNLlsQIBw==
|
||||
|
||||
"@napi-rs/canvas-linux-arm64-musl@0.1.80":
|
||||
version "0.1.80"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.80.tgz#d8ccd91f31d70760628623cd575134ada17690a3"
|
||||
@@ -847,6 +872,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.84.tgz#8b02c46c5dbb0a58de87885c61ca1681b1199697"
|
||||
integrity sha512-VyZq0EEw+OILnWk7G3ZgLLPaz1ERaPP++jLjeyLMbFOF+Tr4zHzWKiKDsEV/cT7btLPZbVoR3VX+T9/QubnURQ==
|
||||
|
||||
"@napi-rs/canvas-linux-arm64-musl@0.1.87":
|
||||
version "0.1.87"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.87.tgz#31355f0debf35848851be97aa1136905db9cef2a"
|
||||
integrity sha512-CUa5YJjpsFcUxJbtfoQ4bqO/Rq+JU/2RfTNFxx07q1AjuDjCM8+MOOLCvVOV1z3qhl6nKAtjJT0pA0J8EbnK8Q==
|
||||
|
||||
"@napi-rs/canvas-linux-riscv64-gnu@0.1.80":
|
||||
version "0.1.80"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.80.tgz#927a3b859a0e3c691beaf52a19bc4736c4ffc9b8"
|
||||
@@ -857,6 +887,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.84.tgz#787c9c207f69aaa51b852c7063a6eed2985b7fca"
|
||||
integrity sha512-PSMTh8DiThvLRsbtc/a065I/ceZk17EXAATv9uNvHgkgo7wdEfTh2C3aveNkBMGByVO3tvnvD5v/YFtZL07cIg==
|
||||
|
||||
"@napi-rs/canvas-linux-riscv64-gnu@0.1.87":
|
||||
version "0.1.87"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.87.tgz#031d3d30cf667586ad742c5846a0e086c7ada950"
|
||||
integrity sha512-5KM4dBFEzFMNkJV2rheIQWpd+mRZA7VNDnxTT7nsCEf6DUjUnf6Hssq9bAwjVYTe4jqraDHbWRbF4uXLBLRFJg==
|
||||
|
||||
"@napi-rs/canvas-linux-x64-gnu@0.1.80":
|
||||
version "0.1.80"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.80.tgz#25c0416bcedd6fadc15295e9afa8d9697232050c"
|
||||
@@ -867,6 +902,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.84.tgz#fb6eaea81ce679575b5004bc2177ce2d9222642b"
|
||||
integrity sha512-N1GY3noO1oqgEo3rYQIwY44kfM11vA0lDbN0orTOHfCSUZTUyiYCY0nZ197QMahZBm1aR/vYgsWpV74MMMDuNA==
|
||||
|
||||
"@napi-rs/canvas-linux-x64-gnu@0.1.87":
|
||||
version "0.1.87"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.87.tgz#9fcc1dd2574aebe7e57797196712106665948dd0"
|
||||
integrity sha512-zSv+ozz9elT5YhocyogX5LwVYURChO4QGD6CQIW6OnuNA0UOMDD/b4wDzlJiMphISy3EVTntlKFhe4W3EuKcxw==
|
||||
|
||||
"@napi-rs/canvas-linux-x64-musl@0.1.80":
|
||||
version "0.1.80"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.80.tgz#de85d644e09120a60996bbe165cc2efee804551b"
|
||||
@@ -877,6 +917,16 @@
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.84.tgz#54a37352a0be7f2a7218b6f11d8ae9b5cdbb3d6c"
|
||||
integrity sha512-vUZmua6ADqTWyHyei81aXIt9wp0yjeNwTH0KdhdeoBb6azHmFR8uKTukZMXfLCC3bnsW0t4lW7K78KNMknmtjg==
|
||||
|
||||
"@napi-rs/canvas-linux-x64-musl@0.1.87":
|
||||
version "0.1.87"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.87.tgz#170b7fe76f1b52a947bc287cd3c84f15d210b863"
|
||||
integrity sha512-jTNmicAZQ70X+cbjZz6G6w8lmORwxRBmj/U20ECNYvcWVLshgyCKWPFL2I0Z6pkJve0vZWls6oZ15iccm1sv8w==
|
||||
|
||||
"@napi-rs/canvas-win32-arm64-msvc@0.1.87":
|
||||
version "0.1.87"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.87.tgz#6536946c5a1796b0781700ee6b39ac18fc0e96b2"
|
||||
integrity sha512-p6J7UNAxKHYc7AL0glEtYuW/E0OLLUNnLti8dA2OT51p08Il4T7yZCl+iNo6f73HntFP+dgOHh2cTXUhmk8GuA==
|
||||
|
||||
"@napi-rs/canvas-win32-x64-msvc@0.1.80":
|
||||
version "0.1.80"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.80.tgz#6bb95885d9377912d71f1372fc1916fb54d6ef0a"
|
||||
@@ -887,6 +937,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.84.tgz#e0039b89f8e04287c77bd1fb5e6fa671d6c9d3c8"
|
||||
integrity sha512-YSs8ncurc1xzegUMNnQUTYrdrAuaXdPMOa+iYYyAxydOtg0ppV386hyYMsy00Yip1NlTgLCseRG4sHSnjQx6og==
|
||||
|
||||
"@napi-rs/canvas-win32-x64-msvc@0.1.87":
|
||||
version "0.1.87"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.87.tgz#058b13a9dd3a6180bdfa976b7c8814cfb9df9c8f"
|
||||
integrity sha512-WrwfETMLBRFWkGU8fXU50gCpA2eIjR4NE9JyTKl86Kz5g6SDp0CcuqS2phYtB66TI2HDUhTPbNrk4V7Qf1FOLA==
|
||||
|
||||
"@napi-rs/canvas@0.1.80":
|
||||
version "0.1.80"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas/-/canvas-0.1.80.tgz#53615bea56fd94e07331ab13caa7a39efc4914ab"
|
||||
@@ -919,6 +974,23 @@
|
||||
"@napi-rs/canvas-linux-x64-musl" "0.1.84"
|
||||
"@napi-rs/canvas-win32-x64-msvc" "0.1.84"
|
||||
|
||||
"@napi-rs/canvas@^0.1.87":
|
||||
version "0.1.87"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/canvas/-/canvas-0.1.87.tgz#028e581af4499ee4ca569eb10cb5705526fee68d"
|
||||
integrity sha512-Zb5tePmPMOYBcuNW3NQaVM1sIkvIfel39euiOab/XMjC5Oc/AnPJLa/BacJcToGyIvehecS6eqcsF7i0Wqe1Sw==
|
||||
optionalDependencies:
|
||||
"@napi-rs/canvas-android-arm64" "0.1.87"
|
||||
"@napi-rs/canvas-darwin-arm64" "0.1.87"
|
||||
"@napi-rs/canvas-darwin-x64" "0.1.87"
|
||||
"@napi-rs/canvas-linux-arm-gnueabihf" "0.1.87"
|
||||
"@napi-rs/canvas-linux-arm64-gnu" "0.1.87"
|
||||
"@napi-rs/canvas-linux-arm64-musl" "0.1.87"
|
||||
"@napi-rs/canvas-linux-riscv64-gnu" "0.1.87"
|
||||
"@napi-rs/canvas-linux-x64-gnu" "0.1.87"
|
||||
"@napi-rs/canvas-linux-x64-musl" "0.1.87"
|
||||
"@napi-rs/canvas-win32-arm64-msvc" "0.1.87"
|
||||
"@napi-rs/canvas-win32-x64-msvc" "0.1.87"
|
||||
|
||||
"@next/env@16.0.7":
|
||||
version "16.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@next/env/-/env-16.0.7.tgz#eda56377a865d890d25122257d2b8a85b81d6d3d"
|
||||
@@ -1312,6 +1384,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276"
|
||||
integrity sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==
|
||||
|
||||
"@toolsycc/json-repair@^0.1.22":
|
||||
version "0.1.22"
|
||||
resolved "https://registry.yarnpkg.com/@toolsycc/json-repair/-/json-repair-0.1.22.tgz#7ad0eb30c4ef1c4286ad3487dc1bbda562f09986"
|
||||
integrity sha512-IMrsxovS9a5pWGRxMCDQDW8FKKEZI/yK/HMcyJlbnd/s+Mk0dRtGr1BFicL276gDsPvb/JfNHtHSi1oc0eY1jA==
|
||||
|
||||
"@types/better-sqlite3@^7.6.12":
|
||||
version "7.6.12"
|
||||
resolved "https://registry.yarnpkg.com/@types/better-sqlite3/-/better-sqlite3-7.6.12.tgz#e5712d46d71097dcc2775c0b068072eadc15deb7"
|
||||
|
||||
Reference in New Issue
Block a user