Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
recursive-include ms_agent/tools/code_interpreter *.ttf
recursive-include ms_agent/utils *.tiktoken
recursive-include ms_agent/utils/nltk *.zip
recursive-include ms_agent/ *.yaml
include README.md
include LICENSE
include requirements.txt
recursive-include requirements *.txt

# Include projects
recursive-include projects *

# Include webui backend
recursive-include webui/backend *.py

# Include webui frontend dist (will be built during setup)
recursive-include webui/frontend/dist *
recursive-include projects *

# Exclude development files
global-exclude *.pyc
global-exclude __pycache__
global-exclude .DS_Store
global-exclude *.so
Comment on lines +1 to +19
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The current MANIFEST.in configuration will likely create a broken source distribution (sdist). It includes webui/frontend/dist, which is a build artifact, but omits the frontend source files (webui/frontend/src, webui/frontend/package.json, etc.).

When building from a source distribution, the setup.py script needs the frontend source files to run npm install and npm run build. Without them, the build will fail.

To fix this, you should include the frontend source files and exclude build artifacts and dependencies from the sdist.

include README.md
include LICENSE
include requirements.txt
recursive-include requirements *.txt

# Include projects
recursive-include projects *

# Include webui backend and frontend sources
recursive-include webui/backend *.py
recursive-include webui/frontend *

# Exclude development files and build artifacts from sdist
global-exclude *.pyc
global-exclude __pycache__
global-exclude .DS_Store
global-exclude *.so
global-exclude webui/frontend/node_modules *
global-exclude webui/frontend/dist *

2 changes: 1 addition & 1 deletion ms_agent/cli/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def execute(self):

if not webui_dir.exists():
import ms_agent
ms_agent_path = Path(ms_agent.__file__).parent.parent
ms_agent_path = Path(ms_agent.__file__).parent
webui_dir = ms_agent_path / 'webui'

if not webui_dir.exists():
Expand Down
108 changes: 99 additions & 9 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ def readme():


def get_version():
namespace = {}
with open(version_file, 'r', encoding='utf-8') as f:
exec(compile(f.read(), version_file, 'exec'))
return locals()['__version__']
exec(compile(f.read(), version_file, 'exec'), namespace)
return namespace['__version__']


def parse_requirements(fname='requirements.txt', with_version=True):
Expand Down Expand Up @@ -126,16 +127,101 @@ def run(self):

# Copy the repository root's `projects/` into the build directory's `ms_agent/projects/`
src = os.path.join(os.path.dirname(__file__), 'projects')
if not os.path.isdir(src):
if os.path.isdir(src):
dst = os.path.join(self.build_lib, 'ms_agent', 'projects')
os.makedirs(os.path.dirname(dst), exist_ok=True)

if os.path.exists(dst):
shutil.rmtree(dst)

shutil.copytree(src, dst)

# Build and copy webui
self._build_and_copy_webui()

def _build_and_copy_webui(self):
"""Build frontend and copy webui files to build directory"""
import subprocess
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

According to PEP 8, imports should be at the top of the file. Please move import subprocess to the top-level imports with os, shutil, etc. This improves readability and makes it easier to see the module's dependencies at a glance.


repo_root = os.path.dirname(__file__)
webui_src = os.path.join(repo_root, 'webui')

if not os.path.isdir(webui_src):
print(
'Warning: webui directory not found, skipping webui packaging')
return

dst = os.path.join(self.build_lib, 'ms_agent', 'projects')
os.makedirs(os.path.dirname(dst), exist_ok=True)
frontend_src = os.path.join(webui_src, 'frontend')
backend_src = os.path.join(webui_src, 'backend')

if os.path.exists(dst):
shutil.rmtree(dst)
# Check if npm is available
try:
subprocess.run(['npm', '--version'],
capture_output=True,
check=True,
timeout=5)
npm_available = True
except (subprocess.CalledProcessError, FileNotFoundError,
subprocess.TimeoutExpired):
npm_available = False
print(
'Warning: npm not found, cannot build frontend. WebUI may not work properly.'
)

shutil.copytree(src, dst)
# Build frontend if npm is available
if npm_available and os.path.isdir(frontend_src):
print('Building frontend with npm...')

# Install dependencies if needed
node_modules = os.path.join(frontend_src, 'node_modules')
if not os.path.exists(node_modules):
print('Installing frontend dependencies...')
try:
subprocess.run(['npm', 'install'],
cwd=frontend_src,
check=True,
timeout=300)
except (subprocess.CalledProcessError,
subprocess.TimeoutExpired) as e:
print(f'Warning: npm install failed: {e}')
return

# Build frontend
try:
subprocess.run(['npm', 'run', 'build'],
cwd=frontend_src,
check=True,
timeout=300)
print('Frontend built successfully')
except (subprocess.CalledProcessError,
subprocess.TimeoutExpired) as e:
print(f'Warning: npm build failed: {e}')
return

# Copy webui to build directory
webui_dst = os.path.join(self.build_lib, 'ms_agent', 'webui')

# Copy backend
if os.path.isdir(backend_src):
backend_dst = os.path.join(webui_dst, 'backend')
if os.path.exists(backend_dst):
shutil.rmtree(backend_dst)
shutil.copytree(backend_src, backend_dst)
print(f'Copied backend to {backend_dst}')

# Copy frontend dist (built files)
frontend_dist_src = os.path.join(frontend_src, 'dist')
if os.path.isdir(frontend_dist_src):
frontend_dst = os.path.join(webui_dst, 'frontend', 'dist')
os.makedirs(os.path.dirname(frontend_dst), exist_ok=True)
if os.path.exists(frontend_dst):
shutil.rmtree(frontend_dst)
shutil.copytree(frontend_dist_src, frontend_dst)
print(f'Copied frontend dist to {frontend_dst}')
else:
print(
'Warning: frontend dist not found, WebUI may not work in production mode'
)


if __name__ == '__main__':
Expand Down Expand Up @@ -171,7 +257,11 @@ def run(self):
include_package_data=True,
cmdclass={'build_py': build_py},
package_data={
'ms_agent': ['projects/**/*'],
'ms_agent': [
'projects/**/*',
'webui/backend/**/*',
'webui/frontend/dist/**/*',
],
'': ['*.h', '*.cpp', '*.cu'],
},
classifiers=[
Expand Down
17 changes: 12 additions & 5 deletions webui/frontend/src/components/ConversationView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {
AccountTree as WorkflowIcon,
} from '@mui/icons-material';
import { motion, AnimatePresence } from 'framer-motion';
import { useSession, Message } from '../context/SessionContext';
import { useSession, Message, Session } from '../context/SessionContext';
import WorkflowProgress from './WorkflowProgress';
import FileProgress from './FileProgress';
import LogViewer from './LogViewer';
Expand Down Expand Up @@ -90,10 +90,19 @@ const ConversationView: React.FC<ConversationViewProps> = ({ showLogs }) => {
const messagesEndRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);
const [fileError, setFileError] = useState<string | null>(null);
const [fileLang, setFileLang] = useState('text');
const [fileUrl, setFileUrl] = useState<string | null>(null);
const [fileKind, setFileKind] = useState<'text' | 'image' | 'video' | 'audio'>('text');

// Check if waiting for user input
const isWaitingForInput = React.useMemo(() => {
return messages.some(m => m.type === 'waiting_input');
}, [messages]);

// Check if input should be enabled
const inputEnabled = React.useMemo(() => {
return !isLoading || isWaitingForInput;
}, [isLoading, isWaitingForInput]);

const getFileKind = (fname: string): 'text' | 'image' | 'video' | 'audio' => {
const ext = fname.split('.').pop()?.toLowerCase() || '';
if (['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp'].includes(ext)) return 'image';
Expand Down Expand Up @@ -198,7 +207,6 @@ const ConversationView: React.FC<ConversationViewProps> = ({ showLogs }) => {

const data = await response.json();
setFileContent(data.content);
setFileLang(data.language || 'text');
setFileUrl(null);
return;
}
Expand All @@ -208,7 +216,6 @@ const ConversationView: React.FC<ConversationViewProps> = ({ showLogs }) => {
`/api/files/stream?path=${encodeURIComponent(path)}&session_id=${encodeURIComponent(sid || '')}`;
setFileUrl(streamUrl);
setFileContent(null);
setFileLang(kind);
} catch (err) {
setFileError(err instanceof Error ? err.message : 'Failed to load file');
} finally {
Expand Down Expand Up @@ -873,7 +880,7 @@ interface MessageBubbleProps {
}

const MessageBubble: React.FC<MessageBubbleProps> = ({
message, isStreaming, sessionStatus, completedSteps,
message, isStreaming,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

While you've removed sessionStatus and completedSteps from being destructured here, they are still part of the MessageBubbleProps interface and are being passed to this component from ConversationView. To fully clean this up and prevent passing unused props, please also remove them from the MessageBubbleProps interface and from the <MessageBubble ... /> invocation in ConversationView.

showRetry, onRetry
}) => {
const theme = useTheme();
Expand Down
Loading