mirror of https://github.com/microsoft/autogen.git
AGS Improvements (Add Test Button in Team Builder View + Others) (#5416)
<!-- Thank you for your contribution! Please review https://microsoft.github.io/autogen/docs/Contribute before opening a pull request. --> <!-- Please add a reviewer to the assignee section when you create a PR. If you don't have the access to it, we will shortly find a reviewer and assign them to your PR. --> ## Why are these changes needed? - Add ability to test teams in Team Builder view - Update Gallery (add deep research default team, fix bug with gallery serialization) - UI fixes - improve drag drop component experience - improve new session experience (single click rather than 3 clicks to create a session) - fix bug with stop reason not being shown in some cases <img width="1738" alt="Image" src="https://github.com/user-attachments/assets/4b895df2-3bad-474e-bec6-4fbcbf1c4346" /> <img width="1761" alt="Image" src="https://github.com/user-attachments/assets/65f52eb9-e926-4168-88fb-d2496c159474" /> <!-- Please give a short summary of the change and the problem this solves. --> ## Related issue number Closes #5392 <!-- For example: "Closes #1234" --> ## Checks - [ ] I've included any doc changes needed for https://microsoft.github.io/autogen/. See https://microsoft.github.io/autogen/docs/Contribute#documentation to build and test documentation locally. - [ ] I've added tests (if relevant) corresponding to the changes introduced in this PR. - [ ] I've made sure all auto checks have passed.
This commit is contained in:
parent
73a7ba5764
commit
9494ac97a0
|
@ -15,29 +15,67 @@ A: You can specify the directory where files are stored by setting the `--appdir
|
|||
|
||||
Yes. AutoGen standardizes on the openai model api format, and you can use any api server that offers an openai compliant endpoint.
|
||||
|
||||
AutoGen Studio is based on declaritive specifications which applies to models as well. Agents can include a model_client field which specifies the model endpoint details including `model`, `api_key`, `base_url`, `model type`.
|
||||
AutoGen Studio is based on declaritive specifications which applies to models as well. Agents can include a model_client field which specifies the model endpoint details including `model`, `api_key`, `base_url`, `model type`. Note, you can define your [model client](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/components/model-clients.html) in python and dump it to a json file for use in AutoGen Studio.
|
||||
|
||||
An example of the openai model client is shown below:
|
||||
In the following sample, we will define an OpenAI, AzureOpenAI and a local model client in python and dump them to a json file.
|
||||
|
||||
```python
|
||||
from autogen_ext.models.openai import AzureOpenAIChatCompletionClient, OpenAIChatCompletionClient
|
||||
from autogen_core.models import ModelInfo
|
||||
|
||||
model_client=OpenAIChatCompletionClient(
|
||||
model="gpt-4o-mini",
|
||||
)
|
||||
print(model_client.dump_component().model_dump_json())
|
||||
|
||||
az_model_client = AzureOpenAIChatCompletionClient(
|
||||
azure_deployment="{your-azure-deployment}",
|
||||
model="gpt-4o",
|
||||
api_version="2024-06-01",
|
||||
azure_endpoint="https://{your-custom-endpoint}.openai.azure.com/",
|
||||
api_key="sk-...",
|
||||
)
|
||||
print(az_model_client.dump_component().model_dump_json())
|
||||
|
||||
mistral_vllm_model = OpenAIChatCompletionClient(
|
||||
model="TheBloke/Mistral-7B-Instruct-v0.2-GGUF",
|
||||
base_url="http://localhost:1234/v1",
|
||||
model_info=ModelInfo(vision=False, function_calling=True, json_output=False, family="unknown"),
|
||||
)
|
||||
print(mistral_vllm_model.dump_component().model_dump_json())
|
||||
```
|
||||
|
||||
OpenAI
|
||||
|
||||
```json
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"model_type": "OpenAIChatCompletionClient",
|
||||
"api_key": "your-api-key"
|
||||
"provider": "autogen_ext.models.openai.OpenAIChatCompletionClient",
|
||||
"component_type": "model",
|
||||
"version": 1,
|
||||
"component_version": 1,
|
||||
"description": "Chat completion client for OpenAI hosted models.",
|
||||
"label": "OpenAIChatCompletionClient",
|
||||
"config": { "model": "gpt-4o-mini" }
|
||||
}
|
||||
```
|
||||
|
||||
An example of the azure openai model client is shown below:
|
||||
Azure OpenAI
|
||||
|
||||
```json
|
||||
{
|
||||
"model": "gpt-4o-mini",
|
||||
"model_type": "AzureOpenAIChatCompletionClient",
|
||||
"azure_deployment": "gpt-4o-mini",
|
||||
"api_version": "2024-02-15-preview",
|
||||
"azure_endpoint": "https://your-endpoint.openai.azure.com/",
|
||||
"api_key": "your-api-key",
|
||||
"component_type": "model"
|
||||
"provider": "autogen_ext.models.openai.AzureOpenAIChatCompletionClient",
|
||||
"component_type": "model",
|
||||
"version": 1,
|
||||
"component_version": 1,
|
||||
"description": "Chat completion client for Azure OpenAI hosted models.",
|
||||
"label": "AzureOpenAIChatCompletionClient",
|
||||
"config": {
|
||||
"model": "gpt-4o",
|
||||
"api_key": "sk-...",
|
||||
"azure_endpoint": "https://{your-custom-endpoint}.openai.azure.com/",
|
||||
"azure_deployment": "{your-azure-deployment}",
|
||||
"api_version": "2024-06-01"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -45,21 +83,27 @@ Have a local model server like Ollama, vLLM or LMStudio that provide an OpenAI c
|
|||
|
||||
```json
|
||||
{
|
||||
"model": "TheBloke/Mistral-7B-Instruct-v0.2-GGUF",
|
||||
"model_type": "OpenAIChatCompletionClient",
|
||||
"base_url": "http://localhost:1234/v1",
|
||||
"api_version": "1.0",
|
||||
"provider": "autogen_ext.models.openai.OpenAIChatCompletionClient",
|
||||
"component_type": "model",
|
||||
"model_capabilities": {
|
||||
"vision": false,
|
||||
"function_calling": true,
|
||||
"json_output": false
|
||||
"version": 1,
|
||||
"component_version": 1,
|
||||
"description": "Chat completion client for OpenAI hosted models.",
|
||||
"label": "OpenAIChatCompletionClient",
|
||||
"config": {
|
||||
"model": "TheBloke/Mistral-7B-Instruct-v0.2-GGUF",
|
||||
"model_info": {
|
||||
"vision": false,
|
||||
"function_calling": true,
|
||||
"json_output": false,
|
||||
"family": "unknown"
|
||||
},
|
||||
"base_url": "http://localhost:1234/v1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```{caution}
|
||||
It is important that you add the `model_capabilities` field to the model client specification for custom models. This is used by the framework instantiate and use the model correctly. Also, the `AssistantAgent` and many other agents in AgentChat require the model to have the `function_calling` capability.
|
||||
It is important that you add the `model_info` field to the model client specification for custom models. This is used by the framework instantiate and use the model correctly. Also, the `AssistantAgent` and many other agents in AgentChat require the model to have the `function_calling` capability.
|
||||
```
|
||||
|
||||
## Q: The server starts but I can't access the UI
|
||||
|
|
|
@ -12,7 +12,9 @@ myst:
|
|||
|
||||
AutoGen Studio is a low-code interface built to help you rapidly prototype AI agents, enhance them with tools, compose them into teams and interact with them to accomplish tasks. It is built on [AutoGen AgentChat](https://microsoft.github.io/autogen) - a high-level API for building multi-agent applications.
|
||||
|
||||

|
||||
> See a video tutorial on AutoGen Studio v0.4 (02/25) - [https://youtu.be/oum6EI7wohM](https://youtu.be/oum6EI7wohM)
|
||||
|
||||
[](https://www.youtube.com/watch?v=oum6EI7wohM)
|
||||
|
||||
Code for AutoGen Studio is on GitHub at [microsoft/autogen](https://github.com/microsoft/autogen/tree/main/python/packages/autogen-studio)
|
||||
|
||||
|
|
|
@ -10,7 +10,9 @@ myst:
|
|||
AutoGen Studio provides a Team Builder interface where developers can define multiple components and behaviors. Users can create teams, add agents to teams, attach tools and models to agents, and define team termination conditions.
|
||||
After defining a team, users can test it in the Playground view to accomplish various tasks through direct interaction.
|
||||
|
||||

|
||||
> See a video tutorial on AutoGen Studio v0.4 (02/25) - [https://youtu.be/oum6EI7wohM](https://youtu.be/oum6EI7wohM)
|
||||
|
||||
[](https://www.youtube.com/watch?v=oum6EI7wohM)
|
||||
|
||||
## Declarative Specification of Componenents
|
||||
|
||||
|
@ -100,8 +102,6 @@ This example shows a team with a single agent, using the `RoundRobinGroupChat` t
|
|||
|
||||
## Building an Agent Team
|
||||
|
||||
<div style="padding:58.13% 0 0 0;position:relative; border-radius:5px; border-bottom:10px"><iframe src="https://player.vimeo.com/video/1043133833?badge=0&autopause=0&player_id=0&app_id=58479" frameborder="0" allow="autoplay; fullscreen; picture-in-picture; clipboard-write; encrypted-media" style="position:absolute;top:0;left:0;width:100%;height:100%;" title="AutoGen Studio v0.4x - Drag and Drop Interface"></iframe></div><script src="https://player.vimeo.com/api/player.js"></script>
|
||||
|
||||
<br/>
|
||||
|
||||
AutoGen Studio integrates closely with all component abstractions provided by AutoGen AgentChat, including {py:class}`~autogen_agentchat.teams`, {py:class}`~autogen_agentchat.agents`, {py:class}`~autogen_core.models`, {py:class}`~autogen_core.tools`, and termination {py:class}`~autogen_agentchat.conditions`.
|
||||
|
@ -117,6 +117,8 @@ Team Builder Operations:
|
|||
- Agents: Add models and tools
|
||||
- Save team configurations
|
||||
|
||||
Note: For each node in the visual builder, you can click on the edit icon (top right) to view and edit the JSON configuration.
|
||||
|
||||
## Gallery - Sharing and Reusing Components
|
||||
|
||||
A Gallery is a collection of components - teams, agents, models, tools, and terminations - that can be shared and reused across projects.
|
||||
|
|
|
@ -148,7 +148,7 @@ def create_default_gallery() -> Gallery:
|
|||
mistral_vllm_model = OpenAIChatCompletionClient(
|
||||
model="TheBloke/Mistral-7B-Instruct-v0.2-GGUF",
|
||||
base_url="http://localhost:1234/v1",
|
||||
model_info=ModelInfo(vision=False, function_calling=True, json_output=False),
|
||||
model_info=ModelInfo(vision=False, function_calling=True, json_output=False, family="unknown"),
|
||||
)
|
||||
builder.add_model(
|
||||
mistral_vllm_model.dump_component(),
|
||||
|
@ -236,6 +236,7 @@ Read the above conversation. Then select the next role from {participants} to pl
|
|||
model_client=base_model,
|
||||
termination_condition=web_termination,
|
||||
)
|
||||
|
||||
builder.add_team(
|
||||
websurfer_team.dump_component(),
|
||||
label="Web Agent Team (Operator)",
|
||||
|
@ -256,8 +257,8 @@ Read the above conversation. Then select the next role from {participants} to pl
|
|||
|
||||
builder.add_tool(
|
||||
tools.fetch_webpage_tool.dump_component(),
|
||||
label="Webpage Generation Tool",
|
||||
description="A tool that generates a webpage from a list of images. Requires beautifulsoup4 html2text library to function.",
|
||||
label="Fetch Webpage Tool",
|
||||
description="A tool that fetches the content of a webpage and converts it to markdown. Requires the requests and beautifulsoup4 library to function.",
|
||||
)
|
||||
|
||||
builder.add_tool(
|
||||
|
@ -272,6 +273,83 @@ Read the above conversation. Then select the next role from {participants} to pl
|
|||
description="A tool that performs Google searches using the Google Custom Search API. Requires the requests library, [GOOGLE_API_KEY, GOOGLE_CSE_ID] to be set, env variable to function.",
|
||||
)
|
||||
|
||||
# Create deep research agent
|
||||
model_client = OpenAIChatCompletionClient(model="gpt-4o", temperature=0.7)
|
||||
|
||||
research_assistant = AssistantAgent(
|
||||
name="research_assistant",
|
||||
description="A research assistant that performs web searches and analyzes information",
|
||||
model_client=model_client,
|
||||
tools=[tools.google_search_tool, tools.fetch_webpage_tool],
|
||||
system_message="""You are a research assistant focused on finding accurate information.
|
||||
Use the google_search tool to find relevant information.
|
||||
Break down complex queries into specific search terms.
|
||||
Always verify information across multiple sources when possible.
|
||||
When you find relevant information, explain why it's relevant and how it connects to the query. When you get feedback from the a verifier agent, use your tools to act on the feedback and make progress.""",
|
||||
)
|
||||
|
||||
verifier = AssistantAgent(
|
||||
name="verifier",
|
||||
description="A verification specialist who ensures research quality and completeness",
|
||||
model_client=model_client,
|
||||
system_message="""You are a research verification specialist.
|
||||
Your role is to:
|
||||
1. Verify that search queries are effective and suggest improvements if needed
|
||||
2. Explore drill downs where needed e.g, if the answer is likely in a link in the returned search results, suggest clicking on the link
|
||||
3. Suggest additional angles or perspectives to explore. Be judicious in suggesting new paths to avoid scope creep or wasting resources, if the task appears to be addressed and we can provide a report, do this and respond with "TERMINATE".
|
||||
4. Track progress toward answering the original question
|
||||
5. When the research is complete, provide a detailed summary in markdown format. For incomplete research, end your message with "CONTINUE RESEARCH". For complete research, end your message with APPROVED.
|
||||
Your responses should be structured as:
|
||||
- Progress Assessment
|
||||
- Gaps/Issues (if any)
|
||||
- Suggestions (if needed)
|
||||
- Next Steps or Final Summary""",
|
||||
)
|
||||
|
||||
summary_agent = AssistantAgent(
|
||||
name="summary_agent",
|
||||
description="A summary agent that provides a detailed markdown summary of the research as a report to the user.",
|
||||
model_client=model_client,
|
||||
system_message="""You are a summary agent. Your role is to provide a detailed markdown summary of the research as a report to the user. Your report should have a reasonable title that matches the research question and should summarize the key details in the results found in natural an actionable manner. The main results/answer should be in the first paragraph.
|
||||
Your report should end with the word "TERMINATE" to signal the end of the conversation.""",
|
||||
)
|
||||
|
||||
termination = TextMentionTermination("TERMINATE") | MaxMessageTermination(max_messages=30)
|
||||
|
||||
selector_prompt = """You are coordinating a research team by selecting the team member to speak/act next. The following team member roles are available:
|
||||
{roles}.
|
||||
The research_assistant performs searches and analyzes information.
|
||||
The verifier evaluates progress and ensures completeness.
|
||||
The summary_agent provides a detailed markdown summary of the research as a report to the user.
|
||||
|
||||
Given the current context, select the most appropriate next speaker.
|
||||
The research_assistant should search and analyze.
|
||||
The verifier should evaluate progress and guide the research (select this role is there is a need to verify/evaluate progress). You should ONLY select the summary_agent role if the research is complete and it is time to generate a report.
|
||||
|
||||
Base your selection on:
|
||||
1. Current stage of research
|
||||
2. Last speaker's findings or suggestions
|
||||
3. Need for verification vs need for new information
|
||||
Read the following conversation. Then select the next role from {participants} to play. Only return the role.
|
||||
|
||||
{history}
|
||||
|
||||
Read the above conversation. Then select the next role from {participants} to play. ONLY RETURN THE ROLE."""
|
||||
|
||||
deep_research_team = SelectorGroupChat(
|
||||
participants=[research_assistant, verifier, summary_agent],
|
||||
model_client=model_client,
|
||||
termination_condition=termination,
|
||||
selector_prompt=selector_prompt,
|
||||
allow_repeated_speaker=True,
|
||||
)
|
||||
|
||||
builder.add_team(
|
||||
deep_research_team.dump_component(),
|
||||
label="Deep Research Team",
|
||||
description="A team that performs deep research using web searches, verification, and summarization.",
|
||||
)
|
||||
|
||||
return builder.build()
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
VERSION = "0.4.0"
|
||||
VERSION = "0.4.1"
|
||||
__version__ = VERSION
|
||||
APP_NAME = "autogenstudio"
|
||||
|
|
|
@ -220,7 +220,7 @@ const Sidebar = ({ link, meta, isMobile }: SidebarProps) => {
|
|||
],
|
||||
})
|
||||
}
|
||||
className="group flex gap-x-3 rounded-md p-2 text-sm font-medium text-primary hover:text-accent hover:bg-secondary justify-center"
|
||||
className="group hidden flex gap-x-3 rounded-md p-2 text-sm font-medium text-primary hover:text-accent hover:bg-secondary justify-center"
|
||||
>
|
||||
<Settings className="h-6 w-6 shrink-0 text-secondary group-hover:text-accent" />
|
||||
</Link>
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -151,7 +151,7 @@ export const useGalleryStore = create<GalleryStore>()(
|
|||
},
|
||||
}),
|
||||
{
|
||||
name: "gallery-storage-v2",
|
||||
name: "gallery-storage-v3",
|
||||
}
|
||||
)
|
||||
);
|
||||
|
|
|
@ -148,8 +148,6 @@ export default function ChatView({ session }: ChatViewProps) {
|
|||
setCurrentRun((current) => {
|
||||
if (!current || !session?.id) return null;
|
||||
|
||||
console.log("WebSocket message:", message);
|
||||
|
||||
switch (message.type) {
|
||||
case "error":
|
||||
if (inputTimeoutRef.current) {
|
||||
|
@ -305,7 +303,6 @@ export default function ChatView({ session }: ChatViewProps) {
|
|||
}
|
||||
|
||||
try {
|
||||
console.log("Sending input response:", response);
|
||||
activeSocketRef.current.send(
|
||||
JSON.stringify({
|
||||
type: "input_response",
|
||||
|
|
|
@ -125,12 +125,6 @@ const RunView: React.FC<RunViewProps> = ({
|
|||
const lastResultMessage = run.team_result?.task_result.messages.slice(-1)[0];
|
||||
const lastMessage = run.messages.slice(-1)[0];
|
||||
|
||||
console.log("lastResultMessage", lastResultMessage);
|
||||
console.log(
|
||||
"lastMessage",
|
||||
run.messages[run.messages.length - 1]?.config?.content
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-6 mr-2 ">
|
||||
{/* Run Header */}
|
||||
|
|
|
@ -128,7 +128,6 @@ export const SessionManager: React.FC = () => {
|
|||
|
||||
const handleQuickStart = async (teamId: number, teamName: string) => {
|
||||
if (!user?.email) return;
|
||||
console.log("Quick start session", teamId, teamName);
|
||||
try {
|
||||
const defaultName = `${teamName.substring(
|
||||
0,
|
||||
|
|
|
@ -71,14 +71,14 @@ const NewSessionControls = ({
|
|||
// Update state first
|
||||
setSelectedTeamId(newTeamId);
|
||||
|
||||
// Save to localStorage
|
||||
if (typeof window !== "undefined") {
|
||||
localStorage.setItem("lastUsedTeamId", e.key);
|
||||
}
|
||||
// // Save to localStorage
|
||||
// if (typeof window !== "undefined") {
|
||||
// localStorage.setItem("lastUsedTeamId", e.key);
|
||||
// }
|
||||
|
||||
// Delay the session start to allow UI to update
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
onStartSession(newTeamId, selectedTeam.component.label || "");
|
||||
// // Delay the session start to allow UI to update
|
||||
// await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
// onStartSession(newTeamId, selectedTeam.component.label || "");
|
||||
};
|
||||
|
||||
const hasNoTeams = !isLoading && teams.length === 0;
|
||||
|
|
|
@ -103,6 +103,10 @@ export const Sidebar: React.FC<SidebarProps> = ({
|
|||
|
||||
<div className="py-2 flex text-sm text-secondary">
|
||||
Recents{" "}
|
||||
<span className="text-accent text-xs mx-1 mt-0.5">
|
||||
{" "}
|
||||
({sessions.length}){" "}
|
||||
</span>{" "}
|
||||
{isLoading && (
|
||||
<RefreshCcw className="w-4 h-4 inline-block ml-2 animate-spin" />
|
||||
)}
|
||||
|
@ -117,7 +121,7 @@ export const Sidebar: React.FC<SidebarProps> = ({
|
|||
</div>
|
||||
)}
|
||||
|
||||
<div className="overflow-y-auto h-[calc(100%-150px)]">
|
||||
<div className="overflow-y-auto scroll h-[calc(100%-181px)]">
|
||||
{sessions.map((s) => (
|
||||
<div key={s.id} className="relative">
|
||||
<div
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
.drop-target-valid {
|
||||
outline: 2px solid #4caf50;
|
||||
outline-offset: 4px;
|
||||
transition: outline-color 0.2s;
|
||||
}
|
||||
|
||||
.drop-target-invalid {
|
||||
outline: 2px dashed #f44336;
|
||||
outline-offset: 4px;
|
||||
}
|
||||
|
||||
.droppable-zone {
|
||||
min-height: 40px;
|
||||
border: 2px dashed #ccc;
|
||||
border-radius: 4px;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.droppable-zone.can-drop {
|
||||
border-color: #4caf50;
|
||||
background: rgba(76, 175, 80, 0.1);
|
||||
}
|
||||
|
||||
.my-left-handle{
|
||||
@apply bg-accent w-1 h-3 rounded-r-sm -ml-1 border-0 !important;
|
||||
outline: 2px solid #4caf50;
|
||||
outline-offset: 4px;
|
||||
transition: outline-color 0.2s;
|
||||
}
|
||||
|
||||
.my-right-handle{
|
||||
@apply bg-accent w-1 h-3 rounded-l-sm -mr-1 border-0 !important;
|
||||
}
|
||||
.drop-target-invalid {
|
||||
outline: 2px dashed #f44336;
|
||||
outline-offset: 4px;
|
||||
}
|
||||
|
||||
.droppable-zone {
|
||||
min-height: 40px;
|
||||
border: 2px dashed #ccc;
|
||||
border-radius: 4px;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.droppable-zone.can-drop {
|
||||
border-color: #4caf50;
|
||||
background: rgba(76, 175, 80, 0.1);
|
||||
}
|
||||
|
||||
.my-left-handle {
|
||||
@apply bg-accent w-1 h-3 rounded-r-sm -ml-1 border-0 !important;
|
||||
}
|
||||
|
||||
.my-right-handle {
|
||||
@apply bg-accent w-1 h-3 rounded-l-sm -mr-1 border-0 !important;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
//team/builder/builder.tsx
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import React, {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import {
|
||||
DndContext,
|
||||
useSensor,
|
||||
|
@ -7,6 +13,8 @@ import {
|
|||
PointerSensor,
|
||||
DragEndEvent,
|
||||
DragOverEvent,
|
||||
DragOverlay, // Add this
|
||||
DragStartEvent, // Add this
|
||||
} from "@dnd-kit/core";
|
||||
import {
|
||||
ReactFlow,
|
||||
|
@ -19,10 +27,10 @@ import {
|
|||
} from "@xyflow/react";
|
||||
import "@xyflow/react/dist/style.css";
|
||||
import { Button, Layout, message, Modal, Switch, Tooltip } from "antd";
|
||||
import { Cable, Code2, Download, Save } from "lucide-react";
|
||||
import { Cable, Code2, Download, PlayCircle, Save } from "lucide-react";
|
||||
import { useTeamBuilderStore } from "./store";
|
||||
import { ComponentLibrary } from "./library";
|
||||
import { ComponentTypes, Team } from "../../../types/datamodel";
|
||||
import { ComponentTypes, Team, Session } from "../../../types/datamodel";
|
||||
import { CustomNode, CustomEdge, DragItem } from "./types";
|
||||
import { edgeTypes, nodeTypes } from "./nodes";
|
||||
|
||||
|
@ -32,8 +40,17 @@ import TeamBuilderToolbar from "./toolbar";
|
|||
import { MonacoEditor } from "../../monaco";
|
||||
import { NodeEditor } from "./node-editor/node-editor";
|
||||
import debounce from "lodash.debounce";
|
||||
import { appContext } from "../../../../hooks/provider";
|
||||
import { sessionAPI } from "../../session/api";
|
||||
import TestDrawer from "./testdrawer";
|
||||
|
||||
const { Sider, Content } = Layout;
|
||||
interface DragItemData {
|
||||
type: ComponentTypes;
|
||||
config: any;
|
||||
label: string;
|
||||
icon: React.ReactNode;
|
||||
}
|
||||
|
||||
interface TeamBuilderProps {
|
||||
team: Team;
|
||||
|
@ -56,6 +73,11 @@ export const TeamBuilder: React.FC<TeamBuilderProps> = ({
|
|||
// const [isDirty, setIsDirty] = useState(false);
|
||||
const editorRef = useRef(null);
|
||||
const [messageApi, contextHolder] = message.useMessage();
|
||||
const [activeDragItem, setActiveDragItem] = useState<DragItemData | null>(
|
||||
null
|
||||
);
|
||||
|
||||
const [testDrawerVisible, setTestDrawerVisible] = useState(false);
|
||||
|
||||
const {
|
||||
undo,
|
||||
|
@ -262,11 +284,23 @@ export const TeamBuilder: React.FC<TeamBuilderProps> = ({
|
|||
|
||||
// Pass both new node data AND target node id
|
||||
addNode(position, draggedItem.config, nodeId);
|
||||
setActiveDragItem(null);
|
||||
};
|
||||
|
||||
const handleTestDrawerClose = () => {
|
||||
console.log("TestDrawer closed");
|
||||
setTestDrawerVisible(false);
|
||||
};
|
||||
|
||||
const onDragStart = (item: DragItem) => {
|
||||
// We can add any drag start logic here if needed
|
||||
};
|
||||
const handleDragStart = (event: DragStartEvent) => {
|
||||
const { active } = event;
|
||||
if (active.data.current) {
|
||||
setActiveDragItem(active.data.current as DragItemData);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
{contextHolder}
|
||||
|
@ -304,6 +338,18 @@ export const TeamBuilder: React.FC<TeamBuilderProps> = ({
|
|||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<Tooltip title="Test Team">
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlayCircle size={18} />}
|
||||
className="p-1.5 mr-2 px-2.5 hover:bg-primary/10 rounded-md text-primary/75 hover:text-primary"
|
||||
onClick={() => {
|
||||
setTestDrawerVisible(true);
|
||||
}}
|
||||
>
|
||||
Test Team
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title="Download Team">
|
||||
<Button
|
||||
type="text"
|
||||
|
@ -344,6 +390,7 @@ export const TeamBuilder: React.FC<TeamBuilderProps> = ({
|
|||
sensors={sensors}
|
||||
onDragEnd={handleDragEnd}
|
||||
onDragOver={handleDragOver}
|
||||
onDragStart={handleDragStart}
|
||||
>
|
||||
<Layout className=" relative bg-primary h-[calc(100vh-239px)] rounded">
|
||||
{!isJsonMode && <ComponentLibrary />}
|
||||
|
@ -423,7 +470,30 @@ export const TeamBuilder: React.FC<TeamBuilderProps> = ({
|
|||
onClose={() => setSelectedNode(null)}
|
||||
/>
|
||||
</Layout>
|
||||
<DragOverlay
|
||||
dropAnimation={{
|
||||
duration: 250,
|
||||
easing: "cubic-bezier(0.18, 0.67, 0.6, 1.22)",
|
||||
}}
|
||||
>
|
||||
{activeDragItem ? (
|
||||
<div className="p-2 text-primary h-full rounded ">
|
||||
<div className="flex items-center gap-2">
|
||||
{activeDragItem.icon}
|
||||
<span className="text-sm">{activeDragItem.label}</span>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</DragOverlay>
|
||||
</DndContext>
|
||||
|
||||
{testDrawerVisible && (
|
||||
<TestDrawer
|
||||
isVisble={testDrawerVisible}
|
||||
team={team}
|
||||
onClose={() => handleTestDrawerClose()}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -51,7 +51,7 @@ const PresetItem: React.FC<PresetItemProps> = ({
|
|||
|
||||
const style = {
|
||||
transform: CSS.Transform.toString(transform),
|
||||
opacity: isDragging ? 0.5 : undefined,
|
||||
opacity: isDragging ? 0.8 : undefined,
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -60,7 +60,7 @@ const PresetItem: React.FC<PresetItemProps> = ({
|
|||
style={style}
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
className="p-2 text-primary mb-2 border border-secondary rounded cursor-move hover:bg-secondary transition-colors "
|
||||
className={`p-2 text-primary mb-2 border rounded cursor-move bg-secondary transition-colors`}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<GripVertical className="w-4 h-4 inline-block" />
|
||||
|
@ -178,7 +178,7 @@ export const ComponentLibrary: React.FC<LibraryProps> = () => {
|
|||
return (
|
||||
<Sider
|
||||
width={300}
|
||||
className="bg-primary z-10 mr-2 border-r border-secondary"
|
||||
className="bg-primary border z-10 mr-2 border-r border-secondary"
|
||||
>
|
||||
<div className="rounded p-2 pt-2">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
import React, { useContext, useEffect, useState } from "react";
|
||||
|
||||
import { Drawer, Button, message, Checkbox } from "antd";
|
||||
import { Team, Session } from "../../../types/datamodel";
|
||||
import ChatView from "../../session/chat/chat";
|
||||
import { appContext } from "../../../../hooks/provider";
|
||||
import { sessionAPI } from "../../session/api";
|
||||
|
||||
interface TestDrawerProps {
|
||||
isVisble: boolean;
|
||||
team: Team;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const TestDrawer = ({ isVisble, onClose, team }: TestDrawerProps) => {
|
||||
const [session, setSession] = useState<Session | null>(null);
|
||||
const { user } = useContext(appContext);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [deleteOnClose, setDeleteOnClose] = useState(true);
|
||||
const [messageApi, contextHolder] = message.useMessage();
|
||||
|
||||
const createSession = async (teamId: number, teamName: string) => {
|
||||
if (!user?.email) return;
|
||||
try {
|
||||
const defaultName = `Test Session ${teamName.substring(
|
||||
0,
|
||||
20
|
||||
)} - ${new Date().toLocaleString()} `;
|
||||
const created = await sessionAPI.createSession(
|
||||
{
|
||||
name: defaultName,
|
||||
team_id: teamId,
|
||||
},
|
||||
user.email
|
||||
);
|
||||
setSession(created);
|
||||
} catch (error) {
|
||||
messageApi.error("Error creating session");
|
||||
}
|
||||
};
|
||||
|
||||
const deleteSession = async (sessionId: number) => {
|
||||
if (!user?.email) return;
|
||||
try {
|
||||
await sessionAPI.deleteSession(sessionId, user.email);
|
||||
setSession(null); // Clear session state after successful deletion
|
||||
} catch (error) {
|
||||
messageApi.error("Error deleting session");
|
||||
}
|
||||
};
|
||||
|
||||
// Single effect to handle session creation when drawer opens
|
||||
useEffect(() => {
|
||||
if (isVisble && team?.id && !session) {
|
||||
setLoading(true);
|
||||
createSession(
|
||||
team.id,
|
||||
team.component.label || team.component.component_type
|
||||
).finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}
|
||||
}, [isVisble, team?.id]);
|
||||
|
||||
// Single cleanup handler in the Drawer's onClose
|
||||
const handleClose = async () => {
|
||||
if (session?.id && deleteOnClose) {
|
||||
// Only delete if flag is true
|
||||
await deleteSession(session.id);
|
||||
}
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{contextHolder}
|
||||
<Drawer
|
||||
title={<span>Test Team: {team.component.label}</span>}
|
||||
size="large"
|
||||
placement="right"
|
||||
onClose={handleClose}
|
||||
open={isVisble}
|
||||
extra={
|
||||
<Checkbox
|
||||
checked={deleteOnClose}
|
||||
onChange={(e) => setDeleteOnClose(e.target.checked)}
|
||||
>
|
||||
Delete session on close
|
||||
</Checkbox>
|
||||
}
|
||||
>
|
||||
{loading && <p>Creating a test session...</p>}
|
||||
{session && <ChatView session={session} />}
|
||||
</Drawer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default TestDrawer;
|
|
@ -255,7 +255,9 @@ export const TeamSidebar: React.FC<TeamSidebarProps> = ({
|
|||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
galleryTeam.label =
|
||||
galleryTeam.label + "_" + new Date().getTime();
|
||||
galleryTeam.label +
|
||||
"_" +
|
||||
(new Date().getTime() + "").substring(0, 5);
|
||||
onCreateTeam({
|
||||
component: galleryTeam,
|
||||
});
|
||||
|
|
|
@ -32,6 +32,7 @@ dependencies = [
|
|||
"alembic",
|
||||
"loguru",
|
||||
"pyyaml",
|
||||
"html2text",
|
||||
"autogen-core>=0.4.5,<0.5",
|
||||
"autogen-agentchat>=0.4.5,<0.5",
|
||||
"autogen-ext[magentic-one, openai, azure]>=0.4.2,<0.5",
|
||||
|
|
|
@ -749,7 +749,7 @@ requires-dist = [
|
|||
|
||||
[[package]]
|
||||
name = "autogenstudio"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
source = { editable = "packages/autogen-studio" }
|
||||
dependencies = [
|
||||
{ name = "aiofiles" },
|
||||
|
@ -759,6 +759,7 @@ dependencies = [
|
|||
{ name = "autogen-ext", extra = ["azure", "magentic-one", "openai"] },
|
||||
{ name = "azure-identity" },
|
||||
{ name = "fastapi", extra = ["standard"] },
|
||||
{ name = "html2text" },
|
||||
{ name = "loguru" },
|
||||
{ name = "numpy" },
|
||||
{ name = "psycopg" },
|
||||
|
@ -790,6 +791,7 @@ requires-dist = [
|
|||
{ name = "azure-identity" },
|
||||
{ name = "fastapi", marker = "extra == 'web'" },
|
||||
{ name = "fastapi", extras = ["standard"] },
|
||||
{ name = "html2text" },
|
||||
{ name = "loguru" },
|
||||
{ name = "numpy", specifier = "<2.0.0" },
|
||||
{ name = "psycopg" },
|
||||
|
|
Loading…
Reference in New Issue