Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Deploy view in AGS #4756

Merged
merged 12 commits into from
Dec 19, 2024
Prev Previous commit
Next Next commit
minor fixes, deploy v1
victordibia committed Dec 18, 2024
commit 9139ddf02515c6e286ee9eb2770e056d836ce02e
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React from "react";
import { Alert } from "antd";
import { CodeSection, copyToClipboard } from "./guides";

const DockerGuide: React.FC = () => {
return (
<div className="max-w-4xl">
<h1 className="tdext-2xl font-bold mb-6">Docker Container Setup</h1>

<Alert
className="mb-6"
message="Prerequisites"
description={
<ul className="list-disc pl-4 mt-2 space-y-1">
<li>Docker installed on your system</li>
<li>Basic knowledge of Docker commands</li>
</ul>
}
type="info"
/>
<CodeSection
title="1. Dockerfile"
description=<div>
AutoGen Studio provides a
<a
href="https://github.com/microsoft/autogen/blob/main/python/packages/autogen-studio/Dockerfile"
target="_blank"
rel="noreferrer"
className="text-accent underline px-1"
>
Dockerfile
</a>
that you can use to build your Docker container.{" "}
</div>
code={`FROM mcr.microsoft.com/devcontainers/python:3.10

WORKDIR /code

RUN pip install -U gunicorn autogenstudio

RUN useradd -m -u 1000 user
USER user
ENV HOME=/home/user \
PATH=/home/user/.local/bin:$PATH \
AUTOGENSTUDIO_APPDIR=/home/user/app

WORKDIR $HOME/app

COPY --chown=user . $HOME/app

CMD gunicorn -w $((2 * $(getconf _NPROCESSORS_ONLN) + 1)) --timeout 12600 -k uvicorn.workers.UvicornWorker autogenstudio.web.app:app --bind "0.0.0.0:8081"`}
onCopy={copyToClipboard}
/>

{/* Build and Run */}
<CodeSection
title="2. Build and Run"
description="Build and run your Docker container:"
code={`docker build -t autogenstudio .
docker run -p 8000:8000 autogenstudio`}
onCopy={copyToClipboard}
/>
</div>
);
};

export default DockerGuide;
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from "react";
import { Alert } from "antd";
import { Copy } from "lucide-react";
import { Guide } from "../types";
import PythonGuide from "./python";
import DockerGuide from "./docker";

interface GuideContentProps {
guide: Guide;
}

export const copyToClipboard = (text: string) => {
navigator.clipboard.writeText(text);
};
export const GuideContent: React.FC<GuideContentProps> = ({ guide }) => {
// Render different content based on guide type and id
switch (guide.id) {
case "python-setup":
return <PythonGuide />;

case "docker-setup":
return <DockerGuide />;

// Add more cases for other guides...

default:
return (
<div className="text-secondary">
A Guide with the title <strong>{guide.title}</strong> is work in
progress!
</div>
);
}
};

interface CodeSectionProps {
title: string;
description?: string | React.ReactNode;
code?: string;
onCopy: (text: string) => void;
}

export const CodeSection: React.FC<CodeSectionProps> = ({
title,
description,
code,
onCopy,
}) => (
<section className="mt-6 bg-seco">
<h2 className="text-md font-semibold mb-3">{title}</h2>
{description && <p className=" mb-3">{description}</p>}
{code && (
<div className="relative bg-secondary text-sm p-4 rounded">
<button
onClick={() => onCopy(code)}
className="absolute right-2 top-2 p-2 hover:bg-secondary rounded-md"
>
<Copy className="w-4 h-4 hover:text-accent transition duration-100" />
</button>
<pre className="font-mono text-sm whitespace-pre overflow-auto scroll rounded pb-2">
{code}
</pre>
</div>
)}
</section>
);

export default GuideContent;
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from "react";
import { Alert } from "antd";
import { CodeSection, copyToClipboard } from "./guides";
import { Download } from "lucide-react";

const PythonGuide: React.FC = () => {
return (
<div className="max-w-4xl">
<h1 className="tdext-2xl font-bold mb-6">
Using AutoGen Studio Teams in Python Code
</h1>

<Alert
className="mb-6"
message="Prerequisites"
description={
<ul className="list-disc pl-4 mt-2 space-y-1">
<li>Python 3.8 or higher</li>
<li>pip package manager</li>
<li>Virtual environment (recommended)</li>
</ul>
}
type="info"
/>

<div className="my-3 text-sm">
{" "}
You can reuse the declarative specifications of agent teams created in
AutoGen studio in your python application by using the TeamManager
class.{" "}
</div>

{/* Installation Steps */}
<div className="space-y-6">
{/* Virtual Environment Setup */}
<CodeSection
title="1. Download the team configuration"
description=<div>
In AutoGen Studio, select a team configuration and click download.{" "}
<Download className="h-4 w-4 inline-block" />{" "}
</div>
code={``}
onCopy={copyToClipboard}
/>

{/* Basic Usage */}
<CodeSection
title="2. Run a task with the team configuration"
description="Here's a simple example of using the TeamManager:"
code={`from autogenstudio.teammanager import TeamManager

# Initialize the TeamManager
manager = TeamManager()

# Run a task with a specific team configuration
result = await manager.run(
task="What is the weather in New York?",
team_config="team.json"
)
print(result)`}
onCopy={copyToClipboard}
/>
</div>
</div>
);
};

export default PythonGuide;
Original file line number Diff line number Diff line change
@@ -1,7 +1,86 @@
import React, { useState } from "react";
import React, { useState, useEffect } from "react";
import { ChevronRight, TriangleAlert } from "lucide-react";
import { DeploySidebar } from "./sidebar";
import { Guide, defaultGuides } from "./types";
import { GuideContent } from "./guides/guides";

export const DeployManager: React.FC = () => {
return <div className="relative flex h-full w-full">{/* Sidebar */}</div>;
const [isLoading, setIsLoading] = useState(false);
const [guides, setGuides] = useState<Guide[]>(defaultGuides);
const [currentGuide, setCurrentGuide] = useState<Guide | null>(null);
const [isSidebarOpen, setIsSidebarOpen] = useState(() => {
if (typeof window !== "undefined") {
const stored = localStorage.getItem("deploySidebar");
return stored !== null ? JSON.parse(stored) : true;
}
return true;
});

// Persist sidebar state
useEffect(() => {
if (typeof window !== "undefined") {
localStorage.setItem("deploySidebar", JSON.stringify(isSidebarOpen));
}
}, [isSidebarOpen]);

// Set first guide as current if none selected
useEffect(() => {
if (!currentGuide && guides.length > 0) {
setCurrentGuide(guides[0]);
}
}, [guides, currentGuide]);

return (
<div className="relative flex h-full w-full">
{/* Sidebar */}
<div
className={`absolute left-0 top-0 h-full transition-all duration-200 ease-in-out ${
isSidebarOpen ? "w-64" : "w-12"
}`}
>
<DeploySidebar
isOpen={isSidebarOpen}
guides={guides}
currentGuide={currentGuide}
onToggle={() => setIsSidebarOpen(!isSidebarOpen)}
onSelectGuide={setCurrentGuide}
isLoading={isLoading}
/>
</div>

{/* Main Content */}
<div
className={`flex-1 transition-all -mr-6 duration-200 ${
isSidebarOpen ? "ml-64" : "ml-12"
}`}
>
<div className="p-4 pt-2">
{/* Breadcrumb */}
<div className="flex items-center gap-2 mb-4 text-sm">
<span className="text-primary font-medium">Deploy</span>
{currentGuide && (
<>
<ChevronRight className="w-4 h-4 text-secondary" />
<span className="text-secondary">{currentGuide.title}</span>
</>
)}
</div>
<div className="rounded border border-secondary border-dashed p-2 text-sm mb-4">
<TriangleAlert className="w-4 h-4 inline-block mr-2 -mt-1 text-secondary " />{" "}
The deployment guide section is work in progress.
</div>
{/* Content Area */}
{currentGuide ? (
<GuideContent guide={currentGuide} />
) : (
<div className="flex items-center justify-center h-[calc(100vh-190px)] text-secondary">
Select a guide from the sidebar to get started
</div>
)}
</div>
</div>
</div>
);
};

export default DeployManager;
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import React from "react";
import { Button, Tooltip } from "antd";
import {
PanelLeftClose,
PanelLeftOpen,
Book,
InfoIcon,
RefreshCcw,
} from "lucide-react";
import type { Guide } from "./types";

interface DeploySidebarProps {
isOpen: boolean;
guides: Guide[];
currentGuide: Guide | null;
onToggle: () => void;
onSelectGuide: (guide: Guide) => void;
isLoading?: boolean;
}

export const DeploySidebar: React.FC<DeploySidebarProps> = ({
isOpen,
guides,
currentGuide,
onToggle,
onSelectGuide,
isLoading = false,
}) => {
// Render collapsed state
if (!isOpen) {
return (
<div className="h-full border-r border-secondary">
<div className="p-2 -ml-2">
<Tooltip title="Documentation">
<button
onClick={onToggle}
className="p-2 rounded-md hover:bg-secondary hover:text-accent text-secondary transition-colors focus:outline-none focus:ring-2 focus:ring-accent focus:ring-opacity-50"
>
<PanelLeftOpen strokeWidth={1.5} className="h-6 w-6" />
</button>
</Tooltip>
</div>
</div>
);
}

return (
<div className="h-full border-r border-secondary">
{/* Header */}
<div className="flex items-center justify-between pt-0 p-4 pl-2 pr-2 border-b border-secondary">
<div className="flex items-center gap-2">
{/* <Book className="w-4 h-4" /> */}
<span className="text-primary font-medium">Guides</span>
{/* <span className="px-2 py-0.5 text-xs bg-accent/10 text-accent rounded">
{guides.length}
</span> */}
</div>
<Tooltip title="Close Sidebar">
<button
onClick={onToggle}
className="p-2 rounded-md hover:bg-secondary hover:text-accent text-secondary transition-colors focus:outline-none focus:ring-2 focus:ring-accent focus:ring-opacity-50"
>
<PanelLeftClose strokeWidth={1.5} className="h-6 w-6" />
</button>
</Tooltip>
</div>

{/* Loading State */}
{isLoading && (
<div className="p-4">
<RefreshCcw className="w-4 h-4 inline-block animate-spin" />
</div>
)}

{/* Empty State */}
{!isLoading && guides.length === 0 && (
<div className="p-2 m-2 text-center text-secondary text-sm border border-dashed rounded">
<InfoIcon className="w-4 h-4 inline-block mr-1.5 -mt-0.5" />
No deployment guide available
</div>
)}

{/* Guides List */}
<div className="overflow-y-auto h-[calc(100%-64px)] mt-4">
{guides.map((guide) => (
<div key={guide.id} className="relative">
<div
className={`absolute top-1 left-0.5 z-50 h-[calc(100%-8px)]
w-1 bg-opacity-80 rounded ${
currentGuide?.id === guide.id ? "bg-accent" : "bg-tertiary"
}`}
/>
<div
className={`group ml-1 flex flex-col p-2 rounded-l cursor-pointer hover:bg-secondary ${
currentGuide?.id === guide.id
? "border-accent bg-secondary"
: "border-transparent"
}`}
onClick={() => onSelectGuide(guide)}
>
{/* Guide Title */}
<div className="flex items-center justify-between">
<span className="text-sm truncate">{guide.title}</span>
</div>
</div>
</div>
))}
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export interface Guide {
id: string;
title: string;
type: "python" | "docker" | "cloud";
}

export const defaultGuides: Guide[] = [
{
id: "python-setup",
title: "Python",
type: "python",
},
{
id: "docker-setup",
title: "Docker",
type: "docker",
},
{
id: "cloud-deploy",
title: "Cloud",
type: "cloud",
},
];
Original file line number Diff line number Diff line change
@@ -292,7 +292,7 @@ export const TeamBuilder: React.FC<TeamBuilderProps> = ({
</span>
</div>
<div>
<Tooltip title="Download Team Configuration">
<Tooltip title="Download Team">
<Button
type="text"
icon={<Download size={18} />}
Original file line number Diff line number Diff line change
@@ -116,11 +116,9 @@ export const TeamSidebar: React.FC<TeamSidebarProps> = ({
</div>

{/* Section Label */}
<div className="py-2 text-sm text-secondary">
Recents
{isLoading && (
<RefreshCcw className="w-4 h-4 inline-block ml-2 animate-spin" />
)}
<div className="py-2 flex text-sm text-secondary">
<div className="flex"> Recents</div>
{isLoading && <RefreshCcw className="w-4 h-4 ml-2 animate-spin" />}
</div>

{/* Teams List */}
@@ -137,9 +135,7 @@ export const TeamSidebar: React.FC<TeamSidebarProps> = ({
{teams.length > 0 && (
<div
key={"teams_title"}
className={` ${
isLoading ? "opacity-50 pointer-events-none" : ""
}`}
className={` ${isLoading ? " pointer-events-none" : ""}`}
>
{" "}
{teams.map((team) => (