mirror of
https://github.com/enricoros/big-AGI.git
synced 2026-05-10 21:50:14 -07:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f9d30114c5 | |||
| 564cf0fed0 | |||
| dee9492d4c | |||
| 6ae026f7c5 | |||
| 6bcbe286f3 | |||
| 6f35f72607 | |||
| 3a7aa75538 | |||
| e4e7ac260a | |||
| b8aaa4bb42 | |||
| 7793e2694b | |||
| 83f2c72f29 |
@@ -21,6 +21,10 @@ Or fork & run on Vercel
|
||||
|
||||
[//]: # (big-AGI is an open book; see the **[ready-to-ship and future ideas](https://github.com/users/enricoros/projects/4/views/2)** in our open roadmap)
|
||||
|
||||
### What's New in 1.16.1 · May 13, 2024 (minor release, models support)
|
||||
|
||||
- Support for the new OpenAI GPT-4o 2024-05-13 model
|
||||
|
||||
### What's New in 1.16.0 · May 9, 2024 · Crystal Clear
|
||||
|
||||
- [Beam](https://big-agi.com/blog/beam-multi-model-ai-reasoning) core and UX improvements based on user feedback
|
||||
|
||||
@@ -10,6 +10,10 @@ by release.
|
||||
- milestone: [1.17.0](https://github.com/enricoros/big-agi/milestone/17)
|
||||
- work in progress: [big-AGI open roadmap](https://github.com/users/enricoros/projects/4/views/2), [help here](https://github.com/users/enricoros/projects/4/views/4)
|
||||
|
||||
### What's New in 1.16.1 · May 13, 2024 (minor release, models support)
|
||||
|
||||
- Support for the new OpenAI GPT-4o 2024-05-13 model
|
||||
|
||||
### What's New in 1.16.0 · May 9, 2024 · Crystal Clear
|
||||
|
||||
- [Beam](https://big-agi.com/blog/beam-multi-model-ai-reasoning) core and UX improvements based on user feedback
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { clerkMiddleware } from '@clerk/nextjs/server';
|
||||
|
||||
export default clerkMiddleware();
|
||||
|
||||
export const config = {
|
||||
matcher: ['/((?!.+.[w]+$|_next).*)', '/', '/(api|trpc)(.*)'],
|
||||
};
|
||||
Generated
+629
-245
File diff suppressed because it is too large
Load Diff
+20
-18
@@ -18,17 +18,19 @@
|
||||
"schema": "src/server/prisma/schema.prisma"
|
||||
},
|
||||
"dependencies": {
|
||||
"@clerk/nextjs": "^5.0.8",
|
||||
"@emotion/cache": "^11.11.0",
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@emotion/server": "^11.11.0",
|
||||
"@emotion/styled": "^11.11.5",
|
||||
"@mui/icons-material": "^5.15.15",
|
||||
"@mui/joy": "^5.0.0-beta.32",
|
||||
"@next/bundle-analyzer": "^14.2.2",
|
||||
"@next/third-parties": "^14.2.2",
|
||||
"@prisma/client": "^5.12.1",
|
||||
"@mui/icons-material": "^5.15.17",
|
||||
"@mui/joy": "^5.0.0-beta.36",
|
||||
"@mui/material": "^5.15.17",
|
||||
"@next/bundle-analyzer": "^14.2.3",
|
||||
"@next/third-parties": "^14.2.3",
|
||||
"@prisma/client": "^5.13.0",
|
||||
"@sanity/diff-match-patch": "^3.1.1",
|
||||
"@t3-oss/env-nextjs": "^0.9.2",
|
||||
"@t3-oss/env-nextjs": "^0.10.1",
|
||||
"@tanstack/react-query": "~4.36.1",
|
||||
"@trpc/client": "10.44.1",
|
||||
"@trpc/next": "10.44.1",
|
||||
@@ -41,46 +43,46 @@
|
||||
"idb-keyval": "^6.2.1",
|
||||
"next": "~14.1.4",
|
||||
"nprogress": "^0.2.0",
|
||||
"pdfjs-dist": "4.1.392",
|
||||
"pdfjs-dist": "4.2.67",
|
||||
"plantuml-encoder": "^1.4.0",
|
||||
"prismjs": "^1.29.0",
|
||||
"react": "^18.2.0",
|
||||
"react": "^18.3.1",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-csv": "^2.2.2",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-katex": "^3.0.1",
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-player": "^2.16.0",
|
||||
"react-resizable-panels": "^2.0.18",
|
||||
"react-resizable-panels": "^2.0.19",
|
||||
"react-timeago": "^7.2.0",
|
||||
"rehype-katex": "^7.0.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"remark-math": "^6.0.0",
|
||||
"sharp": "^0.33.3",
|
||||
"superjson": "^2.2.1",
|
||||
"tesseract.js": "^5.0.5",
|
||||
"tesseract.js": "^5.1.0",
|
||||
"tiktoken": "^1.0.14",
|
||||
"uuid": "^9.0.1",
|
||||
"zod": "^3.23.0",
|
||||
"zod": "^3.23.8",
|
||||
"zustand": "^4.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/puppeteer": "0.0.5",
|
||||
"@types/node": "^20.12.7",
|
||||
"@types/node": "^20.12.11",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@types/plantuml-encoder": "^1.4.2",
|
||||
"@types/prismjs": "^1.26.3",
|
||||
"@types/react": "^18.2.79",
|
||||
"@types/prismjs": "^1.26.4",
|
||||
"@types/react": "^18.3.1",
|
||||
"@types/react-beautiful-dnd": "^13.1.8",
|
||||
"@types/react-csv": "^1.1.10",
|
||||
"@types/react-dom": "^18.2.25",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/react-katex": "^3.0.4",
|
||||
"@types/react-timeago": "^4.1.7",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-next": "14.2.2",
|
||||
"eslint-config-next": "^14.2.3",
|
||||
"prettier": "^3.2.5",
|
||||
"prisma": "^5.12.1",
|
||||
"prisma": "^5.13.0",
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
+15
-12
@@ -13,6 +13,7 @@ import '~/common/styles/GithubMarkdown.css';
|
||||
import '~/common/styles/NProgress.css';
|
||||
import '~/common/styles/app.styles.css';
|
||||
|
||||
import { ProviderAuth } from '~/common/providers/ProviderAuth';
|
||||
import { ProviderBackendCapabilities } from '~/common/providers/ProviderBackendCapabilities';
|
||||
import { ProviderBootstrapLogic } from '~/common/providers/ProviderBootstrapLogic';
|
||||
import { ProviderSingleTab } from '~/common/providers/ProviderSingleTab';
|
||||
@@ -32,18 +33,20 @@ const MyApp = ({ Component, emotionCache, pageProps }: MyAppProps) =>
|
||||
</Head>
|
||||
|
||||
<ProviderTheming emotionCache={emotionCache}>
|
||||
<ProviderSingleTab>
|
||||
<ProviderTRPCQuerySettings>
|
||||
<ProviderBackendCapabilities>
|
||||
{/* ^ SSR boundary */}
|
||||
<ProviderBootstrapLogic>
|
||||
<ProviderSnacks>
|
||||
<Component {...pageProps} />
|
||||
</ProviderSnacks>
|
||||
</ProviderBootstrapLogic>
|
||||
</ProviderBackendCapabilities>
|
||||
</ProviderTRPCQuerySettings>
|
||||
</ProviderSingleTab>
|
||||
<ProviderAuth>
|
||||
<ProviderSingleTab>
|
||||
<ProviderTRPCQuerySettings>
|
||||
<ProviderBackendCapabilities>
|
||||
{/* ^ SSR boundary */}
|
||||
<ProviderBootstrapLogic>
|
||||
<ProviderSnacks>
|
||||
<Component {...pageProps} />
|
||||
</ProviderSnacks>
|
||||
</ProviderBootstrapLogic>
|
||||
</ProviderBackendCapabilities>
|
||||
</ProviderTRPCQuerySettings>
|
||||
</ProviderSingleTab>
|
||||
</ProviderAuth>
|
||||
</ProviderTheming>
|
||||
|
||||
{isVercelFromFrontend && <VercelAnalytics debug={false} />}
|
||||
|
||||
@@ -17,7 +17,7 @@ import { Brand } from '~/common/app.config';
|
||||
import { ROUTE_APP_CHAT, ROUTE_INDEX } from '~/common/app.routes';
|
||||
|
||||
// apps access
|
||||
import { incrementalNewsVersion } from '../../src/apps/news/news.version';
|
||||
import { incrementalNewsVersion, useAppNewsStateStore } from '../../src/apps/news/news.version';
|
||||
|
||||
// capabilities access
|
||||
import { useCapabilityBrowserSpeechRecognition, useCapabilityElevenLabs, useCapabilityTextToImage } from '~/common/components/useCapabilities';
|
||||
@@ -81,7 +81,8 @@ function AppDebug() {
|
||||
const chatsCount = useChatStore.getState().conversations?.length;
|
||||
const uxLabsExperiments = Object.entries(useUXLabsStore.getState()).filter(([_k, v]) => v === true).map(([k, _]) => k).join(', ');
|
||||
const { folders, enableFolders } = useFolderStore.getState();
|
||||
const { lastSeenNewsVersion, usageCount } = useAppStateStore.getState();
|
||||
const { lastSeenNewsVersion } = useAppNewsStateStore.getState();
|
||||
const { usageCount } = useAppStateStore.getState();
|
||||
|
||||
|
||||
// derived state
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -153,7 +153,11 @@ export function AttachmentMenu(props: {
|
||||
{/* Converters: {aConverters.map(((converter, idx) => ` ${converter.id}${(idx === aConverterIdx) ? '*' : ''}`)).join(', ')}*/}
|
||||
{/*</Typography>*/}
|
||||
<Typography level='body-xs'>
|
||||
🡒 {isOutputMissing ? 'empty' : aOutputs.map(output => `${output.type}, ${output.type === 'text-block' ? output.text.length.toLocaleString() : '(base64 image)'} bytes`).join(' · ')}
|
||||
🡒 {isOutputMissing ? 'empty' : aOutputs.map(output => `${output.type}, ${output.type === 'text-block'
|
||||
? output.text.length.toLocaleString()
|
||||
: output.type === 'image-part'
|
||||
? output.base64Url.length.toLocaleString()
|
||||
: '(other)'} bytes`).join(' · ')}
|
||||
</Typography>
|
||||
{!!tokenCountApprox && <Typography level='body-xs'>
|
||||
🡒 {tokenCountApprox.toLocaleString()} tokens
|
||||
|
||||
@@ -2,7 +2,7 @@ import { callBrowseFetchPage } from '~/modules/browse/browse.client';
|
||||
|
||||
import { createBase36Uid } from '~/common/util/textUtils';
|
||||
import { htmlTableToMarkdown } from '~/common/util/htmlTableToMarkdown';
|
||||
import { pdfToText } from '~/common/util/pdfUtils';
|
||||
import { pdfToImageDataURLs, pdfToText } from '~/common/util/pdfUtils';
|
||||
|
||||
import type { Attachment, AttachmentConverter, AttachmentId, AttachmentInput, AttachmentSource } from './store-attachments';
|
||||
import type { ComposerOutputMultiPart } from '../composer.types';
|
||||
@@ -297,7 +297,7 @@ export async function attachmentPerformConversion(attachment: Readonly<Attachmen
|
||||
|
||||
case 'pdf-text':
|
||||
if (!(input.data instanceof ArrayBuffer)) {
|
||||
console.log('Expected ArrayBuffer for PDF converter, got:', typeof input.data);
|
||||
console.log('Expected ArrayBuffer for PDF text converter, got:', typeof input.data);
|
||||
break;
|
||||
}
|
||||
// duplicate the ArrayBuffer to avoid mutation
|
||||
@@ -312,7 +312,29 @@ export async function attachmentPerformConversion(attachment: Readonly<Attachmen
|
||||
break;
|
||||
|
||||
case 'pdf-images':
|
||||
// TODO: extract all pages as individual images
|
||||
if (!(input.data instanceof ArrayBuffer)) {
|
||||
console.log('Expected ArrayBuffer for PDF images converter, got:', typeof input.data);
|
||||
break;
|
||||
}
|
||||
// duplicate the ArrayBuffer to avoid mutation
|
||||
const pdfData2 = new Uint8Array(input.data.slice(0));
|
||||
try {
|
||||
const imageDataURLs = await pdfToImageDataURLs(pdfData2);
|
||||
imageDataURLs.forEach((pdfImg, index) => {
|
||||
outputs.push({
|
||||
type: 'image-part',
|
||||
base64Url: pdfImg.base64Url,
|
||||
metadata: {
|
||||
title: `Page ${index + 1}`,
|
||||
width: pdfImg.width,
|
||||
height: pdfImg.height,
|
||||
},
|
||||
collapsible: false,
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error converting PDF to images:', error);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'image':
|
||||
|
||||
@@ -9,6 +9,13 @@ export type ComposerOutputPart = {
|
||||
// TODO: not implemented yet
|
||||
type: 'image-part',
|
||||
base64Url: string,
|
||||
metadata: {
|
||||
title?: string,
|
||||
generatedBy?: string,
|
||||
altText?: string,
|
||||
width?: number,
|
||||
height?: number,
|
||||
},
|
||||
collapsible: false,
|
||||
};
|
||||
|
||||
|
||||
@@ -60,9 +60,10 @@ export const NewsItems: NewsItem[] = [
|
||||
]
|
||||
}*/
|
||||
{
|
||||
versionCode: '1.16',
|
||||
versionCode: '1.16.1',
|
||||
versionName: 'Crystal Clear',
|
||||
versionDate: new Date('2024-05-09T00:00:00Z'),
|
||||
versionDate: new Date('2024-05-13T19:00:00Z'),
|
||||
// versionDate: new Date('2024-05-09T00:00:00Z'),
|
||||
versionCoverImage: coverV116,
|
||||
items: [
|
||||
{ text: <><B href={beamBlogUrl} wow>Beam</B> core and UX improvements based on user feedback</>, issue: 470, icon: ChatBeamIcon },
|
||||
@@ -74,6 +75,7 @@ export const NewsItems: NewsItem[] = [
|
||||
{ text: <>More: <B issue={517}>code soft-wrap</B>, selection toolbar, <B issue={507}>3x faster</B> on Apple silicon</>, issue: 507 },
|
||||
{ text: <>Updated <B>Anthropic</B>*, <B>Groq</B>, <B>Ollama</B>, <B>OpenAI</B>*, <B>OpenRouter</B>*, and <B>Perplexity</B></> },
|
||||
{ text: <>Developers: update LLMs data structures</>, dev: true },
|
||||
{ text: <>1.16.1: Support for <B>OpenAI</B> <B href='https://openai.com/index/hello-gpt-4o/'>GPT-4o</B> (refresh your OpenAI models)</> },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,21 +1,40 @@
|
||||
// NOTE: this is a separate file to help with bundle tracing, as it's included by the ProviderBootstrapLogic (i.e. by All pages)
|
||||
|
||||
// update this variable every time you want to broadcast a new version to clients
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
|
||||
import { useAppStateStore } from '~/common/state/store-appstate';
|
||||
|
||||
|
||||
export const incrementalNewsVersion: number = 15;
|
||||
// update this variable every time you want to broadcast a new version to clients
|
||||
export const incrementalNewsVersion: number = 16.1;
|
||||
|
||||
|
||||
interface NewsState {
|
||||
lastSeenNewsVersion: number;
|
||||
}
|
||||
|
||||
export const useAppNewsStateStore = create<NewsState>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
lastSeenNewsVersion: 0,
|
||||
}),
|
||||
{
|
||||
name: 'app-news',
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
export function shallRedirectToNews() {
|
||||
const { usageCount, lastSeenNewsVersion } = useAppStateStore.getState();
|
||||
const { lastSeenNewsVersion } = useAppNewsStateStore.getState();
|
||||
const { usageCount } = useAppStateStore.getState();
|
||||
const isNewsOutdated = (lastSeenNewsVersion || 0) < incrementalNewsVersion;
|
||||
return isNewsOutdated && usageCount > 2;
|
||||
}
|
||||
|
||||
export function markNewsAsSeen() {
|
||||
const { setLastSeenNewsVersion } = useAppStateStore.getState();
|
||||
setLastSeenNewsVersion(incrementalNewsVersion);
|
||||
useAppNewsStateStore.setState({ lastSeenNewsVersion: incrementalNewsVersion });
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
|
||||
import { useModelsStore } from '~/modules/llms/store-llms';
|
||||
|
||||
import { AgiSquircleIcon } from '~/common/components/icons/AgiSquircleIcon';
|
||||
import { authUserButton } from '~/common/providers/ProviderAuth';
|
||||
import { checkDivider, checkVisibileIcon, NavItemApp, navItems } from '~/common/app.nav';
|
||||
import { themeZIndexDesktopNav } from '~/common/app.theme';
|
||||
|
||||
@@ -184,6 +185,7 @@ export function DesktopNav(props: { component: React.ElementType, currentApp?: N
|
||||
<DesktopNavGroupBox sx={{ mb: 'calc(2 * var(--GroupMarginY))' }}>
|
||||
{navExtLinkItems}
|
||||
{navModalItems}
|
||||
{authUserButton}
|
||||
</DesktopNavGroupBox>
|
||||
|
||||
</InvertedBar>
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import * as React from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { RedirectToSignIn, SignedIn, SignedOut, UserButton } from '@clerk/nextjs';
|
||||
|
||||
|
||||
const enableClerk = !!process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY;
|
||||
|
||||
// Dynamically import ClerkProvider only if ENABLE_CLERK is true
|
||||
const ClerkProviderDynamic = !enableClerk ? null : dynamic(() =>
|
||||
import('@clerk/nextjs')
|
||||
.then(({ ClerkProvider }) => ClerkProvider)
|
||||
.catch(err => {
|
||||
console.error('Failed to load ClerkProvider:', err);
|
||||
const AuthLoadIssue = () => <>Issue Loading Auth</>;
|
||||
AuthLoadIssue.displayName = 'AuthLoadIssue';
|
||||
return AuthLoadIssue;
|
||||
}),
|
||||
);
|
||||
|
||||
export const ProviderAuth = (props: { children: React.ReactNode }) => (enableClerk && ClerkProviderDynamic)
|
||||
? (
|
||||
<ClerkProviderDynamic>
|
||||
<SignedIn>
|
||||
{props.children}
|
||||
</SignedIn>
|
||||
<SignedOut>
|
||||
<RedirectToSignIn />
|
||||
</SignedOut>
|
||||
</ClerkProviderDynamic>
|
||||
) : props.children;
|
||||
|
||||
export const authUserButton = (enableClerk && ClerkProviderDynamic)
|
||||
? <>
|
||||
<UserButton />
|
||||
</>
|
||||
: null;
|
||||
@@ -6,24 +6,13 @@ import { persist } from 'zustand/middleware';
|
||||
|
||||
interface AppStateData {
|
||||
usageCount: number;
|
||||
lastSeenNewsVersion: number;
|
||||
// suppressedItems: Record<string, boolean>;
|
||||
}
|
||||
|
||||
interface AppStateActions {
|
||||
setLastSeenNewsVersion: (version: number) => void;
|
||||
}
|
||||
|
||||
|
||||
export const useAppStateStore = create<AppStateData & AppStateActions>()(
|
||||
export const useAppStateStore = create<AppStateData>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
|
||||
usageCount: 0,
|
||||
lastSeenNewsVersion: 0,
|
||||
// suppressedItems: {},
|
||||
|
||||
setLastSeenNewsVersion: (version: number) => set({ lastSeenNewsVersion: version }),
|
||||
|
||||
}),
|
||||
{
|
||||
|
||||
@@ -32,6 +32,9 @@ interface UIPreferencesStore {
|
||||
renderMarkdown: boolean;
|
||||
setRenderMarkdown: (renderMarkdown: boolean) => void;
|
||||
|
||||
renderCodeSoftWrap: boolean;
|
||||
setRenderCodeSoftWrap: (renderCodeSoftWrap: boolean) => void;
|
||||
|
||||
// showPersonaExamples: boolean;
|
||||
// setShowPersonaExamples: (showPersonaExamples: boolean) => void;
|
||||
|
||||
@@ -75,6 +78,9 @@ export const useUIPreferencesStore = create<UIPreferencesStore>()(
|
||||
renderMarkdown: true,
|
||||
setRenderMarkdown: (renderMarkdown: boolean) => set({ renderMarkdown }),
|
||||
|
||||
renderCodeSoftWrap: false,
|
||||
setRenderCodeSoftWrap: (renderCodeSoftWrap: boolean) => set({ renderCodeSoftWrap }),
|
||||
|
||||
// showPersonaExamples: false,
|
||||
// setShowPersonaExamples: (showPersonaExamples: boolean) => set({ showPersonaExamples }),
|
||||
|
||||
|
||||
@@ -10,12 +10,7 @@
|
||||
* @param pdfBuffer The content of a PDF file
|
||||
*/
|
||||
export async function pdfToText(pdfBuffer: ArrayBuffer): Promise<string> {
|
||||
// Dynamically import the 'pdfjs-dist' library [nextjs]
|
||||
const { getDocument, GlobalWorkerOptions } = await import('pdfjs-dist');
|
||||
|
||||
// Set the worker script path
|
||||
GlobalWorkerOptions.workerSrc = '/workers/pdf.worker.min.mjs';
|
||||
|
||||
const { getDocument } = await dynamicImportPdfJs();
|
||||
const pdf = await getDocument(pdfBuffer).promise;
|
||||
const textPages: string[] = []; // Initialize an array to hold text from all pages
|
||||
|
||||
@@ -25,10 +20,81 @@ export async function pdfToText(pdfBuffer: ArrayBuffer): Promise<string> {
|
||||
const strings = content.items
|
||||
.filter(isTextItem) // Use the type guard to filter out items with the 'str' property
|
||||
.map((item) => (item as { str: string }).str); // Use type assertion to ensure that the item has the 'str' property
|
||||
textPages.push(strings.join(' ') + '\n'); // Add the joined strings to the array
|
||||
|
||||
// textPages.push(strings.join(' ')); // Add the joined strings to the array
|
||||
// New way: join the strings to form a page text. treat empty lines as newlines, otherwise join with a space (or not if the line is just 1 space)
|
||||
textPages.push(strings.reduce((acc, str) => {
|
||||
// empty line -> newline
|
||||
if (str === '')
|
||||
return acc + '\n';
|
||||
|
||||
// single space
|
||||
if (str === ' ')
|
||||
return acc + str;
|
||||
|
||||
// trick: de-hyphenation of consecutive lines
|
||||
if (/\w-$/.test(acc) && /^\w/.test(str))
|
||||
return acc.slice(0, -1) + str;
|
||||
|
||||
// add a space if the last char is not a space or return (regex)
|
||||
if (/\S$/.test(acc))
|
||||
return acc + ' ' + str;
|
||||
|
||||
// otherwise just concatenate
|
||||
return acc + str;
|
||||
}, ''));
|
||||
}
|
||||
return textPages.join('\n\n'); // Join all the page texts at the end
|
||||
}
|
||||
|
||||
|
||||
type PdfPageImage = { base64Url: string, scale: number, width: number, height: number };
|
||||
|
||||
/**
|
||||
* Renders all pages of a PDF to images
|
||||
*
|
||||
* @param pdfBuffer The content of a PDF file
|
||||
* @param scale The scale factor for the image resolution (default 1.5 for moderate quality)
|
||||
*/
|
||||
export async function pdfToImageDataURLs(pdfBuffer: ArrayBuffer, scale = 1.5): Promise<PdfPageImage[]> {
|
||||
const { getDocument } = await dynamicImportPdfJs();
|
||||
const pdf = await getDocument({ data: pdfBuffer }).promise;
|
||||
const images: PdfPageImage[] = [];
|
||||
|
||||
for (let i = 1; i <= pdf.numPages; i++) {
|
||||
const page = await pdf.getPage(i);
|
||||
const viewport = page.getViewport({ scale });
|
||||
const canvas = document.createElement('canvas');
|
||||
const context = canvas.getContext('2d');
|
||||
canvas.height = viewport.height;
|
||||
canvas.width = viewport.width;
|
||||
|
||||
await page.render({
|
||||
canvasContext: context!,
|
||||
viewport,
|
||||
}).promise;
|
||||
|
||||
images.push({
|
||||
base64Url: canvas.toDataURL('image/jpeg'),
|
||||
scale,
|
||||
width: viewport.width,
|
||||
height: viewport.height,
|
||||
});
|
||||
}
|
||||
|
||||
return textPages.join(''); // Join all the page texts at the end
|
||||
return images;
|
||||
}
|
||||
|
||||
|
||||
// Dynamically import the 'pdfjs-dist' library
|
||||
async function dynamicImportPdfJs() {
|
||||
// Dynamically import the 'pdfjs-dist' library [nextjs]
|
||||
const { getDocument, GlobalWorkerOptions } = await import('pdfjs-dist');
|
||||
|
||||
// Set the worker script path
|
||||
GlobalWorkerOptions.workerSrc = '/workers/pdf.worker.min.mjs';
|
||||
|
||||
return { getDocument };
|
||||
}
|
||||
|
||||
// Type guard to check if an item has a 'str' property
|
||||
|
||||
@@ -14,6 +14,7 @@ import { BeamRayGrid } from './scatter/BeamRayGrid';
|
||||
import { BeamScatterInput } from './scatter/BeamScatterInput';
|
||||
import { BeamScatterPane } from './scatter/BeamScatterPane';
|
||||
import { BeamStoreApi, useBeamStore } from './store-beam.hooks';
|
||||
import { useModuleBeamStore } from './store-module-beam';
|
||||
|
||||
|
||||
export function BeamView(props: {
|
||||
@@ -24,6 +25,7 @@ export function BeamView(props: {
|
||||
}) {
|
||||
|
||||
// state
|
||||
const [hasAutoMerged, setHasAutoMerged] = React.useState(false);
|
||||
const [warnIsScattering, setWarnIsScattering] = React.useState(false);
|
||||
|
||||
// external state
|
||||
@@ -36,7 +38,9 @@ export function BeamView(props: {
|
||||
/* root */ inputHistory, inputIssues, inputReady,
|
||||
/* scatter */ isScattering, raysReady,
|
||||
/* gather (composite) */ canGather,
|
||||
/* IDs */ rayIds, fusionIds,
|
||||
} = useBeamStore(props.beamStore, useShallow(state => ({
|
||||
// input
|
||||
inputHistory: state.inputHistory,
|
||||
inputIssues: state.inputIssues,
|
||||
inputReady: state.inputReady,
|
||||
@@ -45,9 +49,13 @@ export function BeamView(props: {
|
||||
raysReady: state.raysReady,
|
||||
// gather (composite)
|
||||
canGather: state.raysReady >= 2 && state.currentFactoryId !== null && state.currentGatherLlmId !== null,
|
||||
// IDs
|
||||
rayIds: state.rays.map(ray => ray.rayId),
|
||||
fusionIds: state.fusions.map(fusion => fusion.fusionId),
|
||||
})));
|
||||
const { gatherAutoStartAfterScatter } = useModuleBeamStore(useShallow(state => ({
|
||||
gatherAutoStartAfterScatter: state.gatherAutoStartAfterScatter,
|
||||
})));
|
||||
const rayIds = useBeamStore(props.beamStore, useShallow(state => state.rays.map(ray => ray.rayId)));
|
||||
const fusionIds = useBeamStore(props.beamStore, useShallow(state => state.fusions.map(fusion => fusion.fusionId)));
|
||||
|
||||
// derived state
|
||||
const raysCount = rayIds.length;
|
||||
@@ -59,6 +67,11 @@ export function BeamView(props: {
|
||||
|
||||
const handleRayIncreaseCount = React.useCallback(() => setRayCount(raysCount + 1), [setRayCount, raysCount]);
|
||||
|
||||
const handleScatterStart = React.useCallback(() => {
|
||||
setHasAutoMerged(false);
|
||||
startScatteringAll();
|
||||
}, [startScatteringAll]);
|
||||
|
||||
|
||||
const handleCreateFusion = React.useCallback(() => {
|
||||
// if scatter is busy, ask for confirmation
|
||||
@@ -70,20 +83,31 @@ export function BeamView(props: {
|
||||
}, [isScattering, props.beamStore]);
|
||||
|
||||
|
||||
const handleStopScatterConfirmation = React.useCallback(() => {
|
||||
const handleStartMergeConfirmation = React.useCallback(() => {
|
||||
setWarnIsScattering(false);
|
||||
stopScatteringAll();
|
||||
handleCreateFusion();
|
||||
}, [handleCreateFusion, stopScatteringAll]);
|
||||
|
||||
const handleStopScatterDenial = React.useCallback(() => setWarnIsScattering(false), []);
|
||||
const handleStartMergeDenial = React.useCallback(() => setWarnIsScattering(false), []);
|
||||
|
||||
|
||||
// (this is great ux) scatter freed up while we were asking the question, proceed
|
||||
// auto-merge
|
||||
const shallAutoMerge = gatherAutoStartAfterScatter && canGather && !isScattering && !hasAutoMerged;
|
||||
React.useEffect(() => {
|
||||
if (warnIsScattering && !isScattering)
|
||||
handleStopScatterConfirmation();
|
||||
}, [handleStopScatterConfirmation, isScattering, warnIsScattering]);
|
||||
if (shallAutoMerge) {
|
||||
setHasAutoMerged(true);
|
||||
handleStartMergeConfirmation();
|
||||
}
|
||||
}, [handleStartMergeConfirmation, shallAutoMerge]);
|
||||
|
||||
// (great ux) scatter finished while the "start merge" (warning) dialog is up: dismiss dialog and proceed
|
||||
// here we assume that 'warnIsScattering' shows the intention of the user to proceed with a merge asap
|
||||
const shallResumeMerge = warnIsScattering && !isScattering && !gatherAutoStartAfterScatter;
|
||||
React.useEffect(() => {
|
||||
if (shallResumeMerge)
|
||||
handleStartMergeConfirmation();
|
||||
}, [handleStartMergeConfirmation, shallResumeMerge]);
|
||||
|
||||
|
||||
// runnning
|
||||
@@ -138,7 +162,7 @@ export function BeamView(props: {
|
||||
setRayCount={handleRaySetCount}
|
||||
startEnabled={inputReady}
|
||||
startBusy={isScattering}
|
||||
onStart={startScatteringAll}
|
||||
onStart={handleScatterStart}
|
||||
onStop={stopScatteringAll}
|
||||
onExplainerShow={explainerShow}
|
||||
/>
|
||||
@@ -163,7 +187,7 @@ export function BeamView(props: {
|
||||
beamStore={props.beamStore}
|
||||
canGather={canGather}
|
||||
isMobile={props.isMobile}
|
||||
onAddFusion={handleCreateFusion}
|
||||
// onAddFusion={handleCreateFusion}
|
||||
raysReady={raysReady}
|
||||
/>
|
||||
|
||||
@@ -184,8 +208,8 @@ export function BeamView(props: {
|
||||
{warnIsScattering && (
|
||||
<ConfirmationModal
|
||||
open
|
||||
onClose={handleStopScatterDenial}
|
||||
onPositive={handleStopScatterConfirmation}
|
||||
onClose={handleStartMergeDenial}
|
||||
onPositive={handleStartMergeConfirmation}
|
||||
// lowStakes
|
||||
noTitleBar
|
||||
confirmationText='Some responses are still being generated. Do you want to stop and proceed with merging the available responses now?'
|
||||
|
||||
@@ -147,7 +147,7 @@ export function BeamFusionGrid(props: {
|
||||
</Box> : (
|
||||
<Typography level='body-sm' sx={{ opacity: 0.8 }}>
|
||||
{/*You need two or more replies for a {currentFactory?.shortLabel?.toLocaleLowerCase() ?? ''} merge.*/}
|
||||
Waiting for multiple Beams.
|
||||
Waiting for multiple responses.
|
||||
</Typography>
|
||||
)}
|
||||
</BeamCard>
|
||||
|
||||
@@ -57,7 +57,7 @@ export function BeamGatherPane(props: {
|
||||
beamStore: BeamStoreApi,
|
||||
canGather: boolean,
|
||||
isMobile: boolean,
|
||||
onAddFusion: () => void,
|
||||
// onAddFusion: () => void,
|
||||
raysReady: number,
|
||||
}) {
|
||||
|
||||
|
||||
@@ -104,6 +104,7 @@ interface GatherStateSlice {
|
||||
|
||||
// derived state (just acts as a cache to avoid re-calculating)
|
||||
isGatheringAny: boolean;
|
||||
// fusionsReady: number;
|
||||
|
||||
}
|
||||
|
||||
@@ -118,6 +119,7 @@ export const reInitGatherStateSlice = (prevFusions: BFusion[], gatherLlmId: DLLM
|
||||
fusions: [],
|
||||
|
||||
isGatheringAny: false,
|
||||
// fusionsReady: 0,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -170,10 +172,12 @@ export const createGatherSlice: StateCreator<RootStoreSlice & ScatterStoreSlice
|
||||
|
||||
// 'or' the status of all fusions
|
||||
const isGatheringAny = newFusions.some(fusionIsFusing);
|
||||
// const fusionsReady = newFusions.filter(fusionIsUsableOutput).length;
|
||||
|
||||
_set({
|
||||
fusions: newFusions,
|
||||
isGatheringAny,
|
||||
// fusionsReady,
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ import DriveFileRenameOutlineRoundedIcon from '@mui/icons-material/DriveFileRena
|
||||
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
||||
import SchoolRoundedIcon from '@mui/icons-material/SchoolRounded';
|
||||
|
||||
import { DEV_MODE_SETTINGS } from '../../../apps/settings-modal/UxLabsSettings';
|
||||
|
||||
import type { DLLMId } from '~/modules/llms/store-llms';
|
||||
|
||||
import type { BeamStoreApi } from '../store-beam.hooks';
|
||||
@@ -69,6 +71,7 @@ export function BeamScatterDropdown(props: {
|
||||
cardScrolling, toggleCardScrolling,
|
||||
scatterShowPrevMessages, toggleScatterShowPrevMessages,
|
||||
scatterShowLettering, toggleScatterShowLettering,
|
||||
gatherAutoStartAfterScatter, toggleGatherAutoStartAfterScatter,
|
||||
gatherShowAllPrompts, toggleGatherShowAllPrompts,
|
||||
} = useModuleBeamStore();
|
||||
|
||||
@@ -163,10 +166,15 @@ export function BeamScatterDropdown(props: {
|
||||
Response Numbers
|
||||
</MenuItem>
|
||||
|
||||
<ListItem onClick={() => handleClearLastConfig()}>
|
||||
<ListItem onClick={DEV_MODE_SETTINGS ? () => handleClearLastConfig() : undefined}>
|
||||
<Typography level='body-sm'>Advanced</Typography>
|
||||
</ListItem>
|
||||
|
||||
<MenuItem onClick={toggleGatherAutoStartAfterScatter}>
|
||||
<ListItemDecorator>{gatherAutoStartAfterScatter && <CheckRoundedIcon />}</ListItemDecorator>
|
||||
Auto-Merge
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={toggleGatherShowAllPrompts}>
|
||||
<ListItemDecorator>{gatherShowAllPrompts && <CheckRoundedIcon />}</ListItemDecorator>
|
||||
Detailed Custom Merge
|
||||
|
||||
@@ -23,6 +23,7 @@ interface ModuleBeamState {
|
||||
cardScrolling: boolean;
|
||||
scatterShowLettering: boolean;
|
||||
scatterShowPrevMessages: boolean;
|
||||
gatherAutoStartAfterScatter: boolean;
|
||||
gatherShowAllPrompts: boolean;
|
||||
}
|
||||
|
||||
@@ -37,6 +38,7 @@ interface ModuleBeamStore extends ModuleBeamState {
|
||||
toggleCardScrolling: () => void;
|
||||
toggleScatterShowLettering: () => void;
|
||||
toggleScatterShowPrevMessages: () => void;
|
||||
toggleGatherAutoStartAfterScatter: () => void;
|
||||
toggleGatherShowAllPrompts: () => void;
|
||||
}
|
||||
|
||||
@@ -50,6 +52,7 @@ export const useModuleBeamStore = create<ModuleBeamStore>()(persist(
|
||||
scatterShowLettering: false,
|
||||
scatterShowPrevMessages: false,
|
||||
gatherShowAllPrompts: false,
|
||||
gatherAutoStartAfterScatter: false,
|
||||
|
||||
|
||||
addPreset: (name, rayLlmIds, gatherLlmId, gatherFactoryId) => _set(state => ({
|
||||
@@ -86,6 +89,8 @@ export const useModuleBeamStore = create<ModuleBeamStore>()(persist(
|
||||
|
||||
toggleScatterShowPrevMessages: () => _set(state => ({ scatterShowPrevMessages: !state.scatterShowPrevMessages })),
|
||||
|
||||
toggleGatherAutoStartAfterScatter: () => _set(state => ({ gatherAutoStartAfterScatter: !state.gatherAutoStartAfterScatter })),
|
||||
|
||||
toggleGatherShowAllPrompts: () => _set(state => ({ gatherShowAllPrompts: !state.gatherShowAllPrompts })),
|
||||
|
||||
}), {
|
||||
|
||||
@@ -12,6 +12,7 @@ import WrapTextIcon from '@mui/icons-material/WrapText';
|
||||
|
||||
import { copyToClipboard } from '~/common/util/clipboardUtils';
|
||||
import { frontendSideFetch } from '~/common/util/clientFetchers';
|
||||
import { useUIPreferencesStore } from '~/common/state/store-ui';
|
||||
|
||||
import type { CodeBlock } from '../blocks';
|
||||
import { ButtonCodePen, isCodePenSupported } from './ButtonCodePen';
|
||||
@@ -107,7 +108,7 @@ function RenderCodeImpl(props: RenderCodeImplProps) {
|
||||
const [showMermaid, setShowMermaid] = React.useState(true);
|
||||
const [showPlantUML, setShowPlantUML] = React.useState(true);
|
||||
const [showSVG, setShowSVG] = React.useState(true);
|
||||
const [softWrap, setSoftWrap] = React.useState(false);
|
||||
const [softWrap, setSoftWrap] = useUIPreferencesStore(state => [state.renderCodeSoftWrap, state.setRenderCodeSoftWrap]);
|
||||
|
||||
// derived props
|
||||
const {
|
||||
@@ -274,7 +275,7 @@ function RenderCodeImpl(props: RenderCodeImplProps) {
|
||||
{/* Soft Wrap toggle */}
|
||||
{(!renderHTML && !renderMermaid && !renderPlantUML && !renderSVG) && (
|
||||
<Tooltip title='Toggle Soft Wrap'>
|
||||
<OverlayButton variant={softWrap ? 'solid' : 'outlined'} onClick={() => setSoftWrap(on => !on)}>
|
||||
<OverlayButton variant={softWrap ? 'solid' : 'outlined'} onClick={() => setSoftWrap(!softWrap)}>
|
||||
<WrapTextIcon />
|
||||
</OverlayButton>
|
||||
</Tooltip>
|
||||
|
||||
@@ -11,11 +11,39 @@ import { wireTogetherAIListOutputSchema } from './togetherai.wiretypes';
|
||||
// [Azure] / [OpenAI]
|
||||
const _knownOpenAIChatModels: ManualMappings = [
|
||||
|
||||
// GPT-4o -> 2024-05-13
|
||||
{
|
||||
idPrefix: 'gpt-4o',
|
||||
label: 'GPT-4o',
|
||||
description: 'Currently points to gpt-4o-2024-05-13.',
|
||||
symLink: 'gpt-4o-2024-05-13',
|
||||
hidden: true,
|
||||
// copied from symlinked
|
||||
contextWindow: 128000,
|
||||
maxCompletionTokens: 4096,
|
||||
trainingDataCutoff: 'Oct 2023',
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_OAI_Json],
|
||||
pricing: { chatIn: 5, chatOut: 15 },
|
||||
benchmark: { cbaElo: 1310 },
|
||||
},
|
||||
{
|
||||
isLatest: true,
|
||||
idPrefix: 'gpt-4o-2024-05-13',
|
||||
label: 'GPT-4o (2024-05-13)',
|
||||
description: 'Advanced, multimodal flagship model that’s cheaper and faster than GPT-4 Turbo.',
|
||||
contextWindow: 128000,
|
||||
maxCompletionTokens: 4096,
|
||||
trainingDataCutoff: 'Oct 2023',
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_OAI_Json],
|
||||
pricing: { chatIn: 5, chatOut: 15 },
|
||||
benchmark: { cbaElo: 1310 },
|
||||
},
|
||||
|
||||
// GPT4 Turbo with Vision -> 2024-04-09
|
||||
{
|
||||
idPrefix: 'gpt-4-turbo',
|
||||
label: 'GPT-4 Turbo',
|
||||
description: 'GPT-4 Turbo with Vision. The latest GPT-4 Turbo model with vision capabilities. Vision requests can now use JSON mode and function calling. Currently points to gpt-4-turbo-2024-04-09.',
|
||||
description: 'New GPT-4 Turbo with Vision. The latest GPT-4 Turbo model with vision capabilities. Vision requests can now use JSON mode and function calling. Currently points to gpt-4-turbo-2024-04-09.',
|
||||
symLink: 'gpt-4-turbo-2024-04-09',
|
||||
hidden: true,
|
||||
// copied from symlinked
|
||||
@@ -27,7 +55,6 @@ const _knownOpenAIChatModels: ManualMappings = [
|
||||
benchmark: { cbaElo: 1261 },
|
||||
},
|
||||
{
|
||||
isLatest: true,
|
||||
idPrefix: 'gpt-4-turbo-2024-04-09',
|
||||
label: 'GPT-4 Turbo (2024-04-09)',
|
||||
description: 'GPT-4 Turbo with Vision model. Vision requests can now use JSON mode and function calling. gpt-4-turbo currently points to this version.',
|
||||
@@ -66,6 +93,7 @@ const _knownOpenAIChatModels: ManualMappings = [
|
||||
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn, LLM_IF_OAI_Json],
|
||||
pricing: { chatIn: 10, chatOut: 30 },
|
||||
benchmark: { cbaElo: 1251 },
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
idPrefix: 'gpt-4-1106-preview', // GPT-4 Turbo preview model
|
||||
|
||||
Reference in New Issue
Block a user