Fix plotly resize
@ -8,5 +8,16 @@ module.exports = {
|
||||
// key: '/path/to/key.pem',
|
||||
// }
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
webpack: {
|
||||
configure: (webpackConfig) => {
|
||||
// Add .ts and .tsx to resolve.extensions
|
||||
webpackConfig.resolve.extensions = [
|
||||
...webpackConfig.resolve.extensions,
|
||||
'.ts',
|
||||
'.tsx',
|
||||
];
|
||||
return webpackConfig;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 17 KiB |
@ -1,8 +1,8 @@
|
||||
import React, { useRef, useCallback } from 'react';
|
||||
import { BrowserRouter as Router, Routes, Route, useLocation } from "react-router-dom";
|
||||
import { SessionWrapper } from "./SessionWrapper";
|
||||
import { Main } from "./Main";
|
||||
import { Snack, SeverityType } from './Snack';
|
||||
import { SessionWrapper } from "./App/SessionWrapper";
|
||||
import { Main } from "./App/Main";
|
||||
import { Snack, SeverityType } from './Components/Snack';
|
||||
|
||||
export function PathRouter({ setSnack }: { setSnack: any }) {
|
||||
const location = useLocation();
|
||||
|
@ -16,21 +16,20 @@ import Box from '@mui/material/Box';
|
||||
import CssBaseline from '@mui/material/CssBaseline';
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
|
||||
import { ConversationHandle } from './Conversation';
|
||||
import { Query } from './ChatQuery';
|
||||
import { Scrollable } from './Scrollable';
|
||||
import { BackstoryPage, BackstoryTabProps } from './BackstoryTab';
|
||||
import { ConversationHandle } from '../Components/Conversation';
|
||||
import { Query } from '../Components/ChatQuery';
|
||||
import { Scrollable } from '../Components/Scrollable';
|
||||
import { BackstoryPage, BackstoryTabProps } from '../Components/BackstoryTab';
|
||||
|
||||
import { HomePage } from './HomePage';
|
||||
import { LoadingPage } from './LoadingPage';
|
||||
import { ResumeBuilderPage } from './ResumeBuilderPage';
|
||||
import { VectorVisualizerPage } from './VectorVisualizer';
|
||||
import { AboutPage } from './AboutPage';
|
||||
import { ControlsPage } from './ControlsPage';
|
||||
import { SetSnackType } from './Snack';
|
||||
import { HomePage } from '../Pages/HomePage';
|
||||
import { LoadingPage } from '../Pages/LoadingPage';
|
||||
import { ResumeBuilderPage } from '../Pages/ResumeBuilderPage';
|
||||
import { VectorVisualizerPage } from '../Pages/VectorVisualizerPage';
|
||||
import { AboutPage } from '../Pages/AboutPage';
|
||||
import { ControlsPage } from '../Pages/ControlsPage';
|
||||
import { SetSnackType } from '../Components/Snack';
|
||||
|
||||
import './Main.css';
|
||||
import './Conversation.css';
|
||||
|
||||
import '@fontsource/roboto/300.css';
|
||||
import '@fontsource/roboto/400.css';
|
@ -1,7 +1,7 @@
|
||||
import { useEffect, useState, useRef } from "react";
|
||||
import { useNavigate, useLocation } from "react-router-dom";
|
||||
import { connectionBase } from './Global';
|
||||
import { SetSnackType } from './Snack';
|
||||
import { connectionBase } from '../Global';
|
||||
import { SetSnackType } from '../Components/Snack';
|
||||
|
||||
const getSessionId = async () => {
|
||||
const response = await fetch(connectionBase + `/api/context`, {
|
@ -17,7 +17,7 @@ import { Query } from './ChatQuery';
|
||||
import './Conversation.css';
|
||||
import { BackstoryTextField, BackstoryTextFieldRef } from './BackstoryTextField';
|
||||
import { BackstoryElementProps } from './BackstoryTab';
|
||||
import { connectionBase } from './Global';
|
||||
import { connectionBase } from '../Global';
|
||||
|
||||
const loadingMessage: BackstoryMessage = { "role": "status", "content": "Establishing connection with server..." };
|
||||
|
@ -17,7 +17,7 @@ import TableContainer from '@mui/material/TableContainer';
|
||||
import TableRow from '@mui/material/TableRow';
|
||||
|
||||
import { Scrollable } from './Scrollable';
|
||||
import { connectionBase } from './Global';
|
||||
import { connectionBase } from '../Global';
|
||||
|
||||
import './VectorVisualizer.css';
|
||||
import { BackstoryPageProps } from './BackstoryTab';
|
||||
@ -66,14 +66,10 @@ interface PlotData {
|
||||
const config: Partial<Plotly.Config> = {
|
||||
responsive: true,
|
||||
autosizable: true,
|
||||
// displayModeBar: false,
|
||||
displaylogo: false,
|
||||
showSendToCloud: false,
|
||||
staticPlot: false,
|
||||
//fillFrame: true,
|
||||
/** if we DO autosize, set the frame margins in percents of plot size */
|
||||
frameMargins: 0,
|
||||
/** mousewheel or two-finger scroll zooms the plot */
|
||||
scrollZoom: true,
|
||||
doubleClick: "reset+autosize",
|
||||
// | "lasso2d"
|
||||
@ -119,7 +115,7 @@ const config: Partial<Plotly.Config> = {
|
||||
};
|
||||
|
||||
const layout: Partial<Plotly.Layout> = {
|
||||
autosize: true,
|
||||
autosize: false,
|
||||
paper_bgcolor: '#FFFFFF', // white
|
||||
plot_bgcolor: '#FFFFFF', // white plot background
|
||||
font: {
|
||||
@ -200,6 +196,22 @@ const VectorVisualizer: React.FC<VectorVisualizerProps> = (props: VectorVisualiz
|
||||
const [node, setNode] = useState<Node | null>(null);
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
||||
const [plotDimensions, setPlotDimensions] = useState({ width: 0, height: 0 });
|
||||
|
||||
/* Force resize of Plotly as it tends to not be the correct size if it is initially rendered
|
||||
* off screen (eg., the VectorVisualizer is not on the tab the app loads to) */
|
||||
useEffect(() => {
|
||||
requestAnimationFrame(() => {
|
||||
const plotContainer = document.querySelector('.plot-container') as HTMLElement;
|
||||
const svgContainer = document?.querySelector('.svg-container') as HTMLElement;
|
||||
if ( plotContainer && svgContainer) {
|
||||
const plotContainerRect = plotContainer.getBoundingClientRect();
|
||||
svgContainer.style.width = `${plotContainerRect.width}px`;
|
||||
svgContainer.style.height = `${plotContainerRect.height}px`;
|
||||
setPlotDimensions({ width: plotContainerRect.width, height: plotContainerRect.height});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Get the collection to visualize
|
||||
useEffect(() => {
|
||||
@ -509,7 +521,7 @@ The scatter graph shows the query in N-dimensional space, mapped to ${view2D ? '
|
||||
height: "100%",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
layout={layout}
|
||||
layout={{...layout, width: plotDimensions.width, height: plotDimensions.height }}
|
||||
/>
|
||||
</Paper>
|
||||
|
||||
@ -611,21 +623,8 @@ The scatter graph shows the query in N-dimensional space, mapped to ${view2D ? '
|
||||
);
|
||||
};
|
||||
|
||||
const VectorVisualizerPage: React.FC<VectorVisualizerProps> = (props: VectorVisualizerProps) => {
|
||||
return <Scrollable
|
||||
autoscroll={false}
|
||||
sx={{
|
||||
maxWidth: "1024px",
|
||||
height: "calc(100vh - 72px)",
|
||||
}}
|
||||
>
|
||||
<VectorVisualizer {...props} />
|
||||
</Scrollable>;
|
||||
};
|
||||
|
||||
export type { VectorVisualizerProps };
|
||||
|
||||
export {
|
||||
VectorVisualizer,
|
||||
VectorVisualizerPage
|
||||
};
|
@ -1,8 +1,8 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
import { Scrollable } from './Scrollable';
|
||||
import { BackstoryPageProps } from './BackstoryTab';
|
||||
import { Document } from './Document';
|
||||
import { Scrollable } from '../Components/Scrollable';
|
||||
import { BackstoryPageProps } from '../Components/BackstoryTab';
|
||||
import { Document } from '../Components/Document';
|
||||
|
||||
const AboutPage = (props: BackstoryPageProps) => {
|
||||
const { sessionId, submitQuery, setSnack, route, setRoute } = props;
|
@ -14,9 +14,8 @@ import Typography from '@mui/material/Typography';
|
||||
// import ResetIcon from '@mui/icons-material/History';
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
|
||||
import { connectionBase } from './Global';
|
||||
import { BackstoryPageProps } from './BackstoryTab';
|
||||
import { restyle } from 'plotly.js';
|
||||
import { connectionBase } from '../Global';
|
||||
import { BackstoryPageProps } from '../Components/BackstoryTab';
|
||||
|
||||
interface ServerTunables {
|
||||
system_prompt: string,
|
@ -4,10 +4,10 @@ import Box from '@mui/material/Box';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import MuiMarkdown from 'mui-markdown';
|
||||
|
||||
import { BackstoryPageProps } from './BackstoryTab';
|
||||
import { Conversation, ConversationHandle } from './Conversation';
|
||||
import { ChatQuery } from './ChatQuery';
|
||||
import { MessageList } from './Message';
|
||||
import { BackstoryPageProps } from '../Components//BackstoryTab';
|
||||
import { Conversation, ConversationHandle } from '../Components/Conversation';
|
||||
import { ChatQuery } from '../Components/ChatQuery';
|
||||
import { MessageList } from '../Components/Message';
|
||||
|
||||
const HomePage = forwardRef<ConversationHandle, BackstoryPageProps>((props: BackstoryPageProps, ref) => {
|
||||
const { sessionId, setSnack, submitQuery } = props;
|
@ -1,6 +1,6 @@
|
||||
import Box from '@mui/material/Box';
|
||||
import { BackstoryPageProps } from './BackstoryTab';
|
||||
import { BackstoryMessage, Message } from './Message';
|
||||
import { BackstoryPageProps } from '../Components/BackstoryTab';
|
||||
import { BackstoryMessage, Message } from '../Components/Message';
|
||||
|
||||
const LoadingPage = (props: BackstoryPageProps) => {
|
||||
const backstoryPreamble: BackstoryMessage = {
|
@ -6,10 +6,10 @@ import {
|
||||
} from '@mui/material';
|
||||
import { SxProps } from '@mui/material';
|
||||
|
||||
import { ChatQuery, Query } from './ChatQuery';
|
||||
import { MessageList, BackstoryMessage } from './Message';
|
||||
import { Conversation } from './Conversation';
|
||||
import { BackstoryPageProps } from './BackstoryTab';
|
||||
import { ChatQuery, Query } from '../Components/ChatQuery';
|
||||
import { MessageList, BackstoryMessage } from '../Components/Message';
|
||||
import { Conversation } from '../Components/Conversation';
|
||||
import { BackstoryPageProps } from '../Components/BackstoryTab';
|
||||
|
||||
import './ResumeBuilderPage.css';
|
||||
|
28
frontend/src/Pages/VectorVisualizerPage.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
|
||||
import { Scrollable } from '../Components/Scrollable';
|
||||
import { VectorVisualizer } from '../Components/VectorVisualizer';
|
||||
import { BackstoryPageProps } from '../Components/BackstoryTab';
|
||||
|
||||
interface VectorVisualizerProps extends BackstoryPageProps {
|
||||
inline?: boolean;
|
||||
rag?: any;
|
||||
};
|
||||
|
||||
const VectorVisualizerPage: React.FC<VectorVisualizerProps> = (props: VectorVisualizerProps) => {
|
||||
return <Scrollable
|
||||
autoscroll={false}
|
||||
sx={{
|
||||
maxWidth: "1024px",
|
||||
height: "calc(100vh - 72px)",
|
||||
}}
|
||||
>
|
||||
<VectorVisualizer {...props} />
|
||||
</Scrollable>;
|
||||
};
|
||||
|
||||
export type { VectorVisualizerProps };
|
||||
|
||||
export {
|
||||
VectorVisualizerPage
|
||||
};
|
1
frontend/src/declarations.d.ts
vendored
@ -1,2 +1 @@
|
||||
declare module 'tsne-js';
|
||||
declare module 'react-plotly.js';
|
@ -1,10 +1,9 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { ThemeProvider } from '@mui/material/styles';
|
||||
import { backstoryTheme } from './BackstoryTheme'; // Adjust path as needed
|
||||
import { backstoryTheme } from './BackstoryTheme';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById('root') as HTMLElement
|
||||
@ -16,8 +15,3 @@ root.render(
|
||||
</ThemeProvider>
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
// to log results (for example: reportWebVitals(console.log))
|
||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
||||
reportWebVitals();
|
||||
|
@ -1,15 +0,0 @@
|
||||
import { ReportHandler } from 'web-vitals';
|
||||
|
||||
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
getCLS(onPerfEntry);
|
||||
getFID(onPerfEntry);
|
||||
getFCP(onPerfEntry);
|
||||
getLCP(onPerfEntry);
|
||||
getTTFB(onPerfEntry);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default reportWebVitals;
|
@ -1,5 +0,0 @@
|
||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom';
|
@ -18,9 +18,10 @@
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
"jsx": "react-jsx",
|
||||
"baseUrl": "src",
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
||||
|
60
src/utils/check_serializable.py
Normal file
@ -0,0 +1,60 @@
|
||||
from pydantic import BaseModel, Field # type: ignore
|
||||
import json
|
||||
from typing import Any, List, Set
|
||||
|
||||
def check_serializable(obj: Any, path: str = "", errors: List[str] = [], visited: Set[int] = set()) -> List[str]:
|
||||
"""
|
||||
Recursively check all fields in an object for non-JSON-serializable types, avoiding infinite recursion.
|
||||
Skips fields in Pydantic models marked with Field(..., exclude=True).
|
||||
Args:
|
||||
obj: The object to inspect (Pydantic model, dict, list, or other).
|
||||
path: The current field path (e.g., 'field1.nested_field').
|
||||
errors: List to collect error messages.
|
||||
visited: Set of object IDs to track visited objects and prevent infinite recursion.
|
||||
Returns:
|
||||
List of error messages for non-serializable fields.
|
||||
"""
|
||||
# Check for circular reference by object ID
|
||||
obj_id = id(obj)
|
||||
if obj_id in visited:
|
||||
errors.append(f"Field '{path}' contains a circular reference, skipping further inspection")
|
||||
return errors
|
||||
|
||||
# Add current object to visited set
|
||||
visited.add(obj_id)
|
||||
|
||||
try:
|
||||
# Handle Pydantic models
|
||||
if isinstance(obj, BaseModel):
|
||||
for field_name, field_info in obj.model_fields.items():
|
||||
# Skip fields marked with exclude=True
|
||||
if field_info.exclude:
|
||||
continue
|
||||
value = getattr(obj, field_name)
|
||||
new_path = f"{path}.{field_name}" if path else field_name
|
||||
check_serializable(value, new_path, errors, visited)
|
||||
|
||||
# Handle dictionaries
|
||||
elif isinstance(obj, dict):
|
||||
for key, value in obj.items():
|
||||
new_path = f"{path}[{key}]" if path else str(key)
|
||||
check_serializable(value, new_path, errors, visited)
|
||||
|
||||
# Handle lists, tuples, or other iterables
|
||||
elif isinstance(obj, (list, tuple)):
|
||||
for i, value in enumerate(obj):
|
||||
new_path = f"{path}[{i}]" if path else str(i)
|
||||
check_serializable(value, new_path, errors, visited)
|
||||
|
||||
# Handle other types (check for JSON serializability)
|
||||
else:
|
||||
try:
|
||||
json.dumps(obj)
|
||||
except (TypeError, OverflowError, ValueError) as e:
|
||||
errors.append(f"Field '{path}' contains non-serializable type: {type(obj)} ({str(e)})")
|
||||
|
||||
finally:
|
||||
# Remove the current object from visited to allow processing in other branches
|
||||
visited.discard(obj_id)
|
||||
|
||||
return errors
|