From 3415859deb410653af9bca6859729c0aace699ec Mon Sep 17 00:00:00 2001 From: Victor Dibia Date: Wed, 17 Apr 2024 15:16:41 -0700 Subject: [PATCH 01/19] update orm branch + accesibility tweaks --- samples/apps/autogen-studio/.gitignore | 1 + samples/apps/autogen-studio/README.md | 19 +- .../autogenstudio/chatmanager.py | 85 +- .../apps/autogen-studio/autogenstudio/cli.py | 5 + .../autogenstudio/models/__init__.py | 0 .../autogen-studio/autogenstudio/models/db.py | 248 ++++ .../autogenstudio/models/dbmanager.py | 425 ++++++ .../autogenstudio/utils/utils.py | 129 +- .../autogen-studio/autogenstudio/web/app.py | 632 ++++---- .../autogenstudio/workflowmanager.py | 206 +-- .../autogen-studio/docs/ara_stockprices.png | Bin 131 -> 198222 bytes .../frontend/src/components/atoms.tsx | 1277 +--------------- .../frontend/src/components/header.tsx | 2 +- .../frontend/src/components/types.ts | 84 +- .../frontend/src/components/utils.ts | 275 ++-- .../src/components/views/builder/agents.tsx | 178 +-- .../src/components/views/builder/models.tsx | 298 ++-- .../src/components/views/builder/skills.tsx | 73 +- .../views/builder/utils/agentconfig.tsx | 478 ++++++ .../views/builder/utils/selectors.tsx | 1320 +++++++++++++++++ .../views/builder/utils/workflowconfig.tsx | 223 +++ .../src/components/views/builder/workflow.tsx | 178 +-- .../components/views/playground/chatbox.tsx | 118 +- .../src/components/views/playground/ra.tsx | 24 +- .../components/views/playground/sessions.tsx | 248 ++-- .../components/views/playground/sidebar.tsx | 4 - .../{workflows.tsx => utiles/selectors.tsx} | 62 +- .../frontend/src/hooks/store.tsx | 6 +- .../frontend/src/images/icon.png | Bin 130 -> 12710 bytes samples/apps/autogen-studio/pyproject.toml | 3 +- 30 files changed, 3977 insertions(+), 2624 deletions(-) create mode 100644 samples/apps/autogen-studio/autogenstudio/models/__init__.py create mode 100644 samples/apps/autogen-studio/autogenstudio/models/db.py create mode 100644 samples/apps/autogen-studio/autogenstudio/models/dbmanager.py create mode 100644 samples/apps/autogen-studio/frontend/src/components/views/builder/utils/agentconfig.tsx create mode 100644 samples/apps/autogen-studio/frontend/src/components/views/builder/utils/selectors.tsx create mode 100644 samples/apps/autogen-studio/frontend/src/components/views/builder/utils/workflowconfig.tsx rename samples/apps/autogen-studio/frontend/src/components/views/playground/{workflows.tsx => utiles/selectors.tsx} (58%) diff --git a/samples/apps/autogen-studio/.gitignore b/samples/apps/autogen-studio/.gitignore index e94e41454a8..a6d01793422 100644 --- a/samples/apps/autogen-studio/.gitignore +++ b/samples/apps/autogen-studio/.gitignore @@ -1,6 +1,7 @@ database.sqlite .cache/* autogenstudio/web/files/user/* +autogenstudio/models/test autogenstudio/web/files/ui/* OAI_CONFIG_LIST scratch/ diff --git a/samples/apps/autogen-studio/README.md b/samples/apps/autogen-studio/README.md index 49f7e3d657b..1ca426feb3d 100644 --- a/samples/apps/autogen-studio/README.md +++ b/samples/apps/autogen-studio/README.md @@ -15,6 +15,8 @@ Code for AutoGen Studio is on GitHub at [microsoft/autogen](https://github.com/m > AutoGen Studio is currently under active development and we are iterating quickly. Kindly consider that we may introduce breaking changes in the releases during the upcoming weeks, and also the `README` might be outdated. We'll update the `README` as soon as we stabilize the API. > [!NOTE] Updates +> April 17: AutoGen Studio database layer is now rewritten to use [SQLModel](https://sqlmodel.tiangolo.com/) (Pydantic + SQLAlchemy). This provides entity linking (skills, models, agents and workflows are linked via association tables) and supports multiple [database backend dialects](https://docs.sqlalchemy.org/en/20/dialects/) supported in SQLAlchemy (SQLite, PostgreSQL, MySQL, Oracle, Microsoft SQL Server). The backend database can be specified a `--database-uri` argument when running the application. For example, `autogenstudio ui --database-uri sqlite:///database.sqlite` for SQLite and `autogenstudio ui --database-uri postgresql://user:password@localhost/dbname` for PostgreSQL. + > March 12: Default directory for AutoGen Studio is now /home//.autogenstudio. You can also specify this directory using the `--appdir` argument when running the application. For example, `autogenstudio ui --appdir /path/to/folder`. This will store the database and other files in the specified directory e.g. `/path/to/folder/database.sqlite`. `.env` files in that directory will be used to set environment variables for the app. ### Capabilities / Roadmap @@ -84,7 +86,14 @@ autogenstudio ui --port 8081 ``` This will start the application on the specified port. Open your web browser and go to `http://localhost:8081/` to begin using AutoGen Studio. -AutoGen Studio also takes a `--host ` argument to specify the host address. By default, it is set to `localhost`. You can also use the `--appdir ` argument to specify the directory where the app files (e.g., database and generated user files) are stored. By default, it is set to the directory where autogen pip package is installed. + +AutoGen Studio also takes several parameters to customize the application: + +- `--host ` argument to specify the host address. By default, it is set to `localhost`. Y +- `--appdir ` argument to specify the directory where the app files (e.g., database and generated user files) are stored. By default, it is set to the a `.autogenstudio` directory in the user's home directory. +- `--port ` argument to specify the port number. By default, it is set to `8080`. +- `--reload` argument to enable auto-reloading of the server when changes are made to the code. By default, it is set to `False`. +- `--database-uri` argument to specify the database URI. Example values include `sqlite:///database.sqlite` for SQLite and `postgresql://user:password@localhost/dbname` for PostgreSQL. If this is not specified, the database URIL defaults to a `database.sqlite` file in the `--appdir` directory. Now that you have AutoGen Studio installed and running, you are ready to explore its capabilities, including defining and modifying agent workflows, interacting with agents and sessions, and expanding agent skills. @@ -98,8 +107,6 @@ AutoGen Studio proposes some high-level concepts. **Skills**: Skills are functions (e.g., Python functions) that describe how to solve a task. In general, a good skill has a descriptive name (e.g. `generate_images`), extensive docstrings and good defaults (e.g., writing out files to disk for persistence and reuse). You can add new skills AutoGen Studio app via the provided UI. At inference time, these skills are made available to the assistant agent as they address your tasks. -AutoGen Studio comes with 3 example skills: `fetch_profile`, `find_papers`, `generate_images`. The default skills, agents and workflows are based on the [dbdefaults.json](autogentstudio/utils/dbdefaults.json) file which is used to initialize the database. - ## Example Usage Consider the following query. @@ -116,8 +123,6 @@ The agent workflow responds by _writing and executing code_ to create a python p > Note: You can also view the debug console that generates useful information to see how the agents are interacting in the background. - - ## Contribution Guide We welcome contributions to AutoGen Studio. We recommend the following general steps to contribute to the project: @@ -134,7 +139,7 @@ We welcome contributions to AutoGen Studio. We recommend the following general s **Q: How do I specify the directory where files(e.g. database) are stored?** -A: You can specify the directory where files are stored by setting the `--appdir` argument when running the application. For example, `autogenstudio ui --appdir /path/to/folder`. This will store the database and other files in the specified directory e.g. `/path/to/folder/database.sqlite`. +A: You can specify the directory where files are stored by setting the `--appdir` argument when running the application. For example, `autogenstudio ui --appdir /path/to/folder`. This will store the database (default) and other files in the specified directory e.g. `/path/to/folder/database.sqlite`. **Q: Where can I adjust the default skills, agent and workflow configurations?** A: You can modify agent configurations directly from the UI or by editing the [dbdefaults.json](autogenstudio/utils/dbdefaults.json) file which is used to initialize the database. @@ -146,7 +151,7 @@ A: To reset your conversation history, you can delete the `database.sqlite` file A: Yes, you can view the generated messages in the debug console of the web UI, providing insights into the agent interactions. Alternatively, you can inspect the `database.sqlite` file for a comprehensive record of messages. **Q: Can I use other models with AutoGen Studio?** -Yes. AutoGen standardizes on the openai model api format, and you can use any api server that offers an openai compliant endpoint. In the AutoGen Studio UI, each agent has an `llm_config` field where you can input your model endpoint details including `model`, `api key`, `base url`, `model type` and `api version`. For Azure OpenAI models, you can find these details in the Azure portal. Note that for Azure OpenAI, the `model` is the deployment name or deployment id, and the `type` is "azure". +Yes. AutoGen standardizes on the openai model api format, and you can use any api server that offers an openai compliant endpoint. In the AutoGen Studio UI, each agent has an `llm_config` field where you can input your model endpoint details including `model`, `api key`, `base url`, `model type` and `api version`. For Azure OpenAI models, you can find these details in the Azure portal. Note that for Azure OpenAI, the `model name` is the deployment id or engine, and the `model type` is "azure". For other OSS models, we recommend using a server such as vllm to instantiate an openai compliant endpoint. **Q: The server starts but I can't access the UI** diff --git a/samples/apps/autogen-studio/autogenstudio/chatmanager.py b/samples/apps/autogen-studio/autogenstudio/chatmanager.py index 674ae3506a2..1c68a4eca9c 100644 --- a/samples/apps/autogen-studio/autogenstudio/chatmanager.py +++ b/samples/apps/autogen-studio/autogenstudio/chatmanager.py @@ -4,14 +4,18 @@ import time from datetime import datetime from queue import Queue -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Dict, List, Optional, Tuple, Union import websockets from fastapi import WebSocket, WebSocketDisconnect -from .datamodel import AgentWorkFlowConfig, Message, SocketMessage -from .utils import extract_successful_code_blocks, get_modified_files, summarize_chat_history -from .workflowmanager import AutoGenWorkFlowManager +from .models.db import Message, SocketMessage, Workflow +from .utils import ( + extract_successful_code_blocks, + get_modified_files, + summarize_chat_history, +) +from .workflowmanager import WorkflowManager class AutoGenChatManager: @@ -41,7 +45,7 @@ def chat( self, message: Message, history: List[Dict[str, Any]], - flow_config: Optional[AgentWorkFlowConfig] = None, + workflow: Any = None, connection_id: Optional[str] = None, user_dir: Optional[str] = None, **kwargs, @@ -59,78 +63,93 @@ def chat( """ # create a working director for workflow based on user_dir/session_id/time_hash - work_dir = os.path.join(user_dir, message.session_id, datetime.now().strftime("%Y%m%d_%H-%M-%S")) + work_dir = os.path.join( + user_dir, + str(message.session_id), + datetime.now().strftime("%Y%m%d_%H-%M-%S"), + ) os.makedirs(work_dir, exist_ok=True) # if no flow config is provided, use the default - if flow_config is None: - raise ValueError("flow_config must be specified") + if workflow is None: + raise ValueError("Workflow must be specified") - flow = AutoGenWorkFlowManager( - config=flow_config, + workflow_manager = WorkflowManager( + workflow=workflow, history=history, work_dir=work_dir, send_message_function=self.send, connection_id=connection_id, ) + workflow = Workflow.model_validate(workflow) + message_text = message.content.strip() start_time = time.time() - flow.run(message=f"{message_text}", clear_history=False) + workflow_manager.run(message=f"{message_text}", clear_history=False) end_time = time.time() metadata = { - "messages": flow.agent_history, - "summary_method": flow_config.summary_method, + "messages": workflow_manager.agent_history, + "summary_method": workflow.summary_method, "time": end_time - start_time, "files": get_modified_files(start_time, end_time, source_dir=work_dir), } - print("Modified files: ", len(metadata["files"])) - - output = self._generate_output(message_text, flow, flow_config) + output = self._generate_output(message_text, workflow_manager, workflow) output_message = Message( user_id=message.user_id, - root_msg_id=message.root_msg_id, role="assistant", content=output, - metadata=json.dumps(metadata), + meta=json.dumps(metadata), session_id=message.session_id, ) return output_message def _generate_output( - self, message_text: str, flow: AutoGenWorkFlowManager, flow_config: AgentWorkFlowConfig + self, + message_text: str, + workflow_manager: WorkflowManager, + workflow: Workflow, ) -> str: """ Generates the output response based on the workflow configuration and agent history. :param message_text: The text of the incoming message. - :param flow: An instance of `AutoGenWorkFlowManager`. + :param flow: An instance of `WorkflowManager`. :param flow_config: An instance of `AgentWorkFlowConfig`. :return: The output response as a string. """ output = "" - if flow_config.summary_method == "last": - successful_code_blocks = extract_successful_code_blocks(flow.agent_history) - last_message = flow.agent_history[-1]["message"]["content"] if flow.agent_history else "" + if workflow.summary_method == "last": + successful_code_blocks = extract_successful_code_blocks(workflow_manager.agent_history) + last_message = ( + workflow_manager.agent_history[-1]["message"]["content"] if workflow_manager.agent_history else "" + ) successful_code_blocks = "\n\n".join(successful_code_blocks) output = (last_message + "\n" + successful_code_blocks) if successful_code_blocks else last_message - elif flow_config.summary_method == "llm": - model = flow.config.receiver.config.llm_config.config_list[0] + elif workflow.summary_method == "llm": + client = workflow_manager.receiver.client status_message = SocketMessage( type="agent_status", - data={"status": "summarizing", "message": "Generating summary of agent dialogue"}, - connection_id=flow.connection_id, + data={ + "status": "summarizing", + "message": "Generating summary of agent dialogue", + }, + connection_id=workflow_manager.connection_id, ) self.send(status_message.dict()) - output = summarize_chat_history(task=message_text, messages=flow.agent_history, model=model) + output = summarize_chat_history( + task=message_text, + messages=workflow_manager.agent_history, + client=client, + ) - elif flow_config.summary_method == "none": + elif workflow.summary_method == "none": output = "" return output @@ -141,7 +160,9 @@ class WebSocketConnectionManager: """ def __init__( - self, active_connections: List[Tuple[WebSocket, str]] = None, active_connections_lock: asyncio.Lock = None + self, + active_connections: List[Tuple[WebSocket, str]] = None, + active_connections_lock: asyncio.Lock = None, ) -> None: """ Initializes WebSocketConnectionManager with an optional list of active WebSocket connections. @@ -185,7 +206,7 @@ async def disconnect_all(self) -> None: for connection, _ in self.active_connections[:]: await self.disconnect(connection) - async def send_message(self, message: Dict, websocket: WebSocket) -> None: + async def send_message(self, message: Union[Dict, str], websocket: WebSocket) -> None: """ Sends a JSON message to a single WebSocket connection. @@ -202,7 +223,7 @@ async def send_message(self, message: Dict, websocket: WebSocket) -> None: print("Error: WebSocket connection closed normally") await self.disconnect(websocket) except Exception as e: - print(f"Error in sending message: {str(e)}") + print(f"Error in sending message: {str(e)}", message) await self.disconnect(websocket) async def broadcast(self, message: Dict) -> None: diff --git a/samples/apps/autogen-studio/autogenstudio/cli.py b/samples/apps/autogen-studio/autogenstudio/cli.py index aafb13317c8..9be3311a0eb 100644 --- a/samples/apps/autogen-studio/autogenstudio/cli.py +++ b/samples/apps/autogen-studio/autogenstudio/cli.py @@ -1,4 +1,5 @@ import os +from typing import Optional import typer import uvicorn @@ -18,6 +19,7 @@ def ui( reload: Annotated[bool, typer.Option("--reload")] = False, docs: bool = False, appdir: str = None, + database_uri: Optional[str] = None, ): """ Run the AutoGen Studio UI. @@ -29,11 +31,14 @@ def ui( reload (bool, optional): Whether to reload the UI on code changes. Defaults to False. docs (bool, optional): Whether to generate API docs. Defaults to False. appdir (str, optional): Path to the AutoGen Studio app directory. Defaults to None. + database-uri (str, optional): Database URI to connect to. Defaults to None. Examples include sqlite:///autogenstudio.db, postgresql://user:password@localhost/autogenstudio. """ os.environ["AUTOGENSTUDIO_API_DOCS"] = str(docs) if appdir: os.environ["AUTOGENSTUDIO_APPDIR"] = appdir + if database_uri: + os.environ["AUTOGENSTUDIO_DATABASE_URI"] = database_uri uvicorn.run( "autogenstudio.web.app:app", diff --git a/samples/apps/autogen-studio/autogenstudio/models/__init__.py b/samples/apps/autogen-studio/autogenstudio/models/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/samples/apps/autogen-studio/autogenstudio/models/db.py b/samples/apps/autogen-studio/autogenstudio/models/db.py new file mode 100644 index 00000000000..2c04053788f --- /dev/null +++ b/samples/apps/autogen-studio/autogenstudio/models/db.py @@ -0,0 +1,248 @@ +from datetime import datetime +from enum import Enum +from typing import Any, Callable, Dict, List, Literal, Optional, Union + +from sqlalchemy import orm +from sqlmodel import ( + JSON, + Column, + DateTime, + Field, + Relationship, + SQLModel, + func, +) +from sqlmodel import ( + Enum as SqlEnum, +) + +SQLModel.model_config["protected_namespaces"] = () +# pylint: disable=protected-access + + +class Message(SQLModel, table=True): + __table_args__ = {"sqlite_autoincrement": True} + id: Optional[int] = Field(default=None, primary_key=True) + created_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), server_default=func.now()), + ) # pylint: disable=not-callable + updated_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), onupdate=func.now()), + ) # pylint: disable=not-callable + user_id: Optional[str] = None + role: str + content: str + session_id: Optional[int] = Field(default=None, foreign_key="session.id") + workflow_id: Optional[int] = Field(default=None, foreign_key="workflow.id") + connection_id: Optional[str] = None + meta: Optional[Dict] = Field(default={}, sa_column=Column(JSON)) + + +class Session(SQLModel, table=True): + __table_args__ = {"sqlite_autoincrement": True} + id: Optional[int] = Field(default=None, primary_key=True) + created_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), server_default=func.now()), + ) # pylint: disable=not-callable + updated_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), onupdate=func.now()), + ) # pylint: disable=not-callable + user_id: Optional[str] = None + workflow_id: Optional[int] = Field(default=None, foreign_key="workflow.id") + name: Optional[str] = None + description: Optional[str] = None + + +class AgentSkillLink(SQLModel, table=True): + __table_args__ = {"sqlite_autoincrement": True} + agent_id: int = Field(default=None, primary_key=True, foreign_key="agent.id") + skill_id: int = Field(default=None, primary_key=True, foreign_key="skill.id") + + +class AgentModelLink(SQLModel, table=True): + __table_args__ = {"sqlite_autoincrement": True} + agent_id: int = Field(default=None, primary_key=True, foreign_key="agent.id") + model_id: int = Field(default=None, primary_key=True, foreign_key="model.id") + + +class Skill(SQLModel, table=True): + __table_args__ = {"sqlite_autoincrement": True} + id: Optional[int] = Field(default=None, primary_key=True) + created_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), server_default=func.now()), + ) # pylint: disable=not-callable + updated_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), onupdate=func.now()), + ) # pylint: disable=not-callable + user_id: Optional[str] = None + name: str + content: str + description: Optional[str] = None + secrets: Optional[Dict] = Field(default={}, sa_column=Column(JSON)) + libraries: Optional[Dict] = Field(default={}, sa_column=Column(JSON)) + agents: List["Agent"] = Relationship(back_populates="skills", link_model=AgentSkillLink) + + +class LLMConfig(SQLModel, table=False): + """Data model for LLM Config for AutoGen""" + + config_list: List[Any] = Field(default_factory=list) + temperature: float = 0 + cache_seed: Optional[Union[int, None]] = None + timeout: Optional[int] = None + max_tokens: Optional[int] = None + extra_body: Optional[dict] = None + + +class Model(SQLModel, table=True): + __table_args__ = {"sqlite_autoincrement": True} + id: Optional[int] = Field(default=None, primary_key=True) + created_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), server_default=func.now()), + ) # pylint: disable=not-callable + updated_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), onupdate=func.now()), + ) # pylint: disable=not-callable + user_id: Optional[str] = None + model: str + api_key: Optional[str] = None + base_url: Optional[str] = None + api_type: Optional[str] = None + api_version: Optional[str] = None + description: Optional[str] = None + agents: List["Agent"] = Relationship(back_populates="models", link_model=AgentModelLink) + + +class AgentConfig(SQLModel, table=False): + name: Optional[str] = None + human_input_mode: str = "NEVER" + max_consecutive_auto_reply: int = 10 + system_message: Optional[str] = None + is_termination_msg: Optional[Union[bool, str, Callable]] = None + code_execution_config: Optional[Union[bool, Dict]] = Field(default=False, sa_column=Column(JSON)) + default_auto_reply: Optional[str] = "" + description: Optional[str] = None + llm_config: Optional[Union[LLMConfig, bool]] = Field(default=False, sa_column=Column(JSON)) + + admin_name: Optional[str] = "Admin" + messages: Optional[List[Dict]] = Field(default_factory=list) + max_round: Optional[int] = 100 + admin_name: Optional[str] = "Admin" + speaker_selection_method: Optional[str] = "auto" + allow_repeat_speaker: Optional[Union[bool, List["AgentConfig"]]] = True + + +class AgentType(str, Enum): + assistant = "assistant" + userproxy = "userproxy" + groupchat = "groupchat" + + +class WorkflowAgentType(str, Enum): + sender = "sender" + receiver = "receiver" + planner = "planner" + + +class WorkflowAgentLink(SQLModel, table=True): + __table_args__ = {"sqlite_autoincrement": True} + workflow_id: int = Field(default=None, primary_key=True, foreign_key="workflow.id") + agent_id: int = Field(default=None, primary_key=True, foreign_key="agent.id") + agent_type: WorkflowAgentType = Field( + default=WorkflowAgentType.sender, + sa_column=Column(SqlEnum(WorkflowAgentType), primary_key=True), + ) + + +class AgentLink(SQLModel, table=True): + __table_args__ = {"sqlite_autoincrement": True} + parent_id: Optional[int] = Field(default=None, foreign_key="agent.id", primary_key=True) + agent_id: Optional[int] = Field(default=None, foreign_key="agent.id", primary_key=True) + + +class Agent(SQLModel, table=True): + __table_args__ = {"sqlite_autoincrement": True} + id: Optional[int] = Field(default=None, primary_key=True) + created_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), server_default=func.now()), + ) # pylint: disable=not-callable + updated_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), onupdate=func.now()), + ) # pylint: disable=not-callable + user_id: Optional[str] = None + type: AgentType = Field(default=AgentType.assistant, sa_column=Column(SqlEnum(AgentType))) + config: AgentConfig = Field(default_factory=AgentConfig, sa_column=Column(JSON)) + skills: List[Skill] = Relationship(back_populates="agents", link_model=AgentSkillLink) + models: List[Model] = Relationship(back_populates="agents", link_model=AgentModelLink) + workflows: List["Workflow"] = Relationship(link_model=WorkflowAgentLink, back_populates="agents") + parents: List["Agent"] = Relationship( + back_populates="agents", + link_model=AgentLink, + sa_relationship_kwargs=dict( + primaryjoin="Agent.id==AgentLink.agent_id", + secondaryjoin="Agent.id==AgentLink.parent_id", + ), + ) + agents: List["Agent"] = Relationship( + back_populates="parents", + link_model=AgentLink, + sa_relationship_kwargs=dict( + primaryjoin="Agent.id==AgentLink.parent_id", + secondaryjoin="Agent.id==AgentLink.agent_id", + ), + ) + + +class WorkFlowType(str, Enum): + twoagents = "twoagents" + groupchat = "groupchat" + + +class WorkFlowSummaryMethod(str, Enum): + last = "last" + none = "none" + llm = "llm" + + +class Workflow(SQLModel, table=True): + __table_args__ = {"sqlite_autoincrement": True} + id: Optional[int] = Field(default=None, primary_key=True) + created_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), server_default=func.now()), + ) # pylint: disable=not-callable + updated_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), onupdate=func.now()), + ) # pylint: disable=not-callable + user_id: Optional[str] = None + name: str + description: str + agents: List[Agent] = Relationship(back_populates="workflows", link_model=WorkflowAgentLink) + type: WorkFlowType = Field(default=WorkFlowType.twoagents, sa_column=Column(SqlEnum(WorkFlowType))) + summary_method: Optional[WorkFlowSummaryMethod] = Field( + default=WorkFlowSummaryMethod.last, + sa_column=Column(SqlEnum(WorkFlowSummaryMethod)), + ) + + +class Response(SQLModel): + message: str + status: bool + data: Optional[Any] = None + + +class SocketMessage(SQLModel, table=False): + connection_id: str + data: Dict[str, Any] + type: str diff --git a/samples/apps/autogen-studio/autogenstudio/models/dbmanager.py b/samples/apps/autogen-studio/autogenstudio/models/dbmanager.py new file mode 100644 index 00000000000..d63afca9258 --- /dev/null +++ b/samples/apps/autogen-studio/autogenstudio/models/dbmanager.py @@ -0,0 +1,425 @@ +import logging +from datetime import datetime +from typing import Optional + +from sqlmodel import Session, SQLModel, and_, create_engine, select + +from .db import ( + Agent, + AgentLink, + AgentModelLink, + AgentSkillLink, + Model, + Response, + Skill, + Workflow, + WorkflowAgentLink, +) + +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + +valid_link_types = ["agent_model", "agent_skill", "agent_agent", "workflow_agent"] + + +class DBManager: + """A class to manage database operations""" + + def __init__(self, engine_uri: str): + self.engine = create_engine(engine_uri) + self.session = Session(self.engine) + + def create_db_and_tables(self): + """Create a new database and tables""" + SQLModel.metadata.create_all(self.engine) + + def upsert(self, model: SQLModel): + """Create a new entity""" + # check if the model exists, update else add + status = True + model_class = type(model) + + try: + existing_model = self.session.exec(select(model_class).where(model_class.id == model.id)).first() + if existing_model: + model.updated_at = datetime.now() + for key, value in model.model_dump().items(): + setattr(existing_model, key, value) + model = existing_model + self.session.add(model) + else: + self.session.add(model) + self.session.commit() + self.session.refresh(model) + except Exception as e: + self.session.rollback() + logger.error("Error while upserting %s", e) + status = False + + response = Response( + message=( + f"{model_class.__name__} Updated Successfully " + if existing_model + else f"{model_class.__name__} Created Successfully" + ), + status=status, + data=model.model_dump(), + ) + + return response + + def _model_to_dict(self, model_obj): + return {col.name: getattr(model_obj, col.name) for col in model_obj.__table__.columns} + + def get( + self, + model_class: SQLModel, + filters: dict = None, + return_json: bool = False, + order: str = "desc", + ): + """List all entities for a user""" + result = [] + try: + if filters: + conditions = [getattr(model_class, col) == value for col, value in filters.items()] + statement = select(model_class).where(and_(*conditions)) + + if hasattr(model_class, "created_at") and order: + if order == "desc": + statement = statement.order_by(model_class.created_at.desc()) + else: + statement = statement.order_by(model_class.created_at.asc()) + else: + statement = select(model_class) + + if return_json: + result = [self._model_to_dict(row) for row in self.session.exec(statement).all()] + else: + result = self.session.exec(statement).all() + except Exception as e: + logger.error("Error while getting %s: %s", model_class, e) + return result + + def delete(self, model_class: SQLModel, filters: dict = None): + """Delete an entity""" + row = None + status_message = "" + status = True + try: + if filters: + conditions = [getattr(model_class, col) == value for col, value in filters.items()] + row = self.session.exec(select(model_class).where(and_(*conditions))).all() + else: + row = self.session.exec(select(model_class)).all() + if row: + for row in row: + self.session.delete(row) + self.session.commit() + status_message = f"{model_class.__name__} Deleted Successfully" + else: + print(f"Row with filters {filters} not found") + logger.info("Row with filters %s not found", filters) + status_message = "Row not found" + except Exception as e: + self.session.rollback() + logger.error("Error while deleting: %s", e) + status_message = f"Error while deleting: {e}" + status = False + response = Response( + message=status_message, + status=status, + data=None, + ) + print(response) + return response + + def get_linked_entities( + self, + link_type: str, + primary_id: int, + return_json: bool = False, + agent_type: Optional[str] = None, + ): + """ + Get all entities linked to the primary entity. + + Args: + link_type (str): The type of link to retrieve, e.g., "agent_model". + primary_id (int): The identifier for the primary model. + return_json (bool): Whether to return the result as a JSON object. + + Returns: + List[SQLModel]: A list of linked entities. + """ + + linked_entities = [] + + if link_type not in valid_link_types: + return [] + + status = True + status_message = "" + try: + if link_type == "agent_model": + agent = self.get(Agent, filters={"id": primary_id})[0] # get the agent + linked_entities = agent.models + elif link_type == "agent_skill": + agent = self.get(Agent, filters={"id": primary_id})[0] + linked_entities = agent.skills + elif link_type == "agent_agent": + agent = self.get(Agent, filters={"id": primary_id})[0] + linked_entities = agent.agents + elif link_type == "workflow_agent": + linked_entities = self.session.exec( + select(Agent) + .join(WorkflowAgentLink) + .where( + WorkflowAgentLink.workflow_id == primary_id, + WorkflowAgentLink.agent_type == agent_type, + ) + ).all() + except Exception as e: + logger.error("Error while getting linked entities: %s", e) + status_message = f"Error while getting linked entities: {e}" + status = False + if return_json: + linked_entities = [self._model_to_dict(row) for row in linked_entities] + + response = Response( + message=status_message, + status=status, + data=linked_entities, + ) + + return response + + def link( + self, + link_type: str, + primary_id: int, + secondary_id: int, + agent_type: Optional[str] = None, + ) -> Response: + """ + Link two entities together. + + Args: + link_type (str): The type of link to create, e.g., "agent_model". + primary_id (int): The identifier for the primary model. + secondary_id (int): The identifier for the secondary model. + agent_type (Optional[str]): The type of agent, e.g., "sender" or receiver. + + Returns: + Response: The response of the linking operation, including success status and message. + """ + + # TBD verify that is creator of the primary entity being linked + status = True + status_message = "" + primary_model = None + secondary_model = None + + if link_type not in valid_link_types: + status = False + status_message = f"Invalid link type: {link_type}. Valid link types are: {valid_link_types}" + else: + try: + if link_type == "agent_model": + primary_model = self.session.exec(select(Agent).where(Agent.id == primary_id)).first() + secondary_model = self.session.exec(select(Model).where(Model.id == secondary_id)).first() + if primary_model is None or secondary_model is None: + status = False + status_message = "One or both entity records do not exist." + else: + # check if the link already exists + existing_link = self.session.exec( + select(AgentModelLink).where( + AgentModelLink.agent_id == primary_id, + AgentModelLink.model_id == secondary_id, + ) + ).first() + if existing_link: # link already exists + return Response( + message=( + f"{secondary_model.__class__.__name__} already linked " + f"to {primary_model.__class__.__name__}" + ), + status=False, + ) + else: + primary_model.models.append(secondary_model) + elif link_type == "agent_agent": + primary_model = self.session.exec(select(Agent).where(Agent.id == primary_id)).first() + secondary_model = self.session.exec(select(Agent).where(Agent.id == secondary_id)).first() + if primary_model is None or secondary_model is None: + status = False + status_message = "One or both entity records do not exist." + else: + # check if the link already exists + existing_link = self.session.exec( + select(AgentLink).where( + AgentLink.parent_id == primary_id, + AgentLink.agent_id == secondary_id, + ) + ).first() + if existing_link: + return Response( + message=( + f"{secondary_model.__class__.__name__} already linked " + f"to {primary_model.__class__.__name__}" + ), + status=False, + ) + else: + primary_model.agents.append(secondary_model) + + elif link_type == "agent_skill": + primary_model = self.session.exec(select(Agent).where(Agent.id == primary_id)).first() + secondary_model = self.session.exec(select(Skill).where(Skill.id == secondary_id)).first() + if primary_model is None or secondary_model is None: + status = False + status_message = "One or both entity records do not exist." + else: + # check if the link already exists + existing_link = self.session.exec( + select(AgentSkillLink).where( + AgentSkillLink.agent_id == primary_id, + AgentSkillLink.skill_id == secondary_id, + ) + ).first() + if existing_link: + return Response( + message=( + f"{secondary_model.__class__.__name__} already linked " + f"to {primary_model.__class__.__name__}" + ), + status=False, + ) + else: + primary_model.skills.append(secondary_model) + elif link_type == "workflow_agent": + primary_model = self.session.exec(select(Workflow).where(Workflow.id == primary_id)).first() + secondary_model = self.session.exec(select(Agent).where(Agent.id == secondary_id)).first() + if primary_model is None or secondary_model is None: + status = False + status_message = "One or both entity records do not exist." + else: + # check if the link already exists + existing_link = self.session.exec( + select(WorkflowAgentLink).where( + WorkflowAgentLink.workflow_id == primary_id, + WorkflowAgentLink.agent_id == secondary_id, + WorkflowAgentLink.agent_type == agent_type, + ) + ).first() + if existing_link: + return Response( + message=( + f"{secondary_model.__class__.__name__} already linked " + f"to {primary_model.__class__.__name__}" + ), + status=False, + ) + else: + # primary_model.agents.append(secondary_model) + workflow_agent_link = WorkflowAgentLink( + workflow_id=primary_id, + agent_id=secondary_id, + agent_type=agent_type, + ) + self.session.add(workflow_agent_link) + # add and commit the link + self.session.add(primary_model) + self.session.commit() + status_message = ( + f"{secondary_model.__class__.__name__} successfully linked " + f"to {primary_model.__class__.__name__}" + ) + + except Exception as e: + self.session.rollback() + logger.error("Error while linking: %s", e) + status = False + status_message = f"Error while linking due to an exception: {e}" + + response = Response( + message=status_message, + status=status, + ) + + return response + + def unlink( + self, + link_type: str, + primary_id: int, + secondary_id: int, + agent_type: Optional[str] = None, + ) -> Response: + """ + Unlink two entities. + + Args: + link_type (str): The type of link to remove, e.g., "agent_model". + primary_id (int): The identifier for the primary model. + secondary_id (int): The identifier for the secondary model. + agent_type (Optional[str]): The type of agent, e.g., "sender" or receiver. + + Returns: + Response: The response of the unlinking operation, including success status and message. + """ + status = True + status_message = "" + + if link_type not in valid_link_types: + status = False + status_message = f"Invalid link type: {link_type}. Valid link types are: {valid_link_types}" + return Response(message=status_message, status=status) + + try: + if link_type == "agent_model": + existing_link = self.session.exec( + select(AgentModelLink).where( + AgentModelLink.agent_id == primary_id, + AgentModelLink.model_id == secondary_id, + ) + ).first() + elif link_type == "agent_skill": + existing_link = self.session.exec( + select(AgentSkillLink).where( + AgentSkillLink.agent_id == primary_id, + AgentSkillLink.skill_id == secondary_id, + ) + ).first() + elif link_type == "agent_agent": + existing_link = self.session.exec( + select(AgentLink).where( + AgentLink.parent_id == primary_id, + AgentLink.agent_id == secondary_id, + ) + ).first() + elif link_type == "workflow_agent": + existing_link = self.session.exec( + select(WorkflowAgentLink).where( + WorkflowAgentLink.workflow_id == primary_id, + WorkflowAgentLink.agent_id == secondary_id, + WorkflowAgentLink.agent_type == agent_type, + ) + ).first() + + if existing_link: + self.session.delete(existing_link) + self.session.commit() + status_message = "Link removed successfully." + else: + status = False + status_message = "Link does not exist." + + except Exception as e: + self.session.rollback() + logger.error("Error while unlinking: %s", e) + status = False + status_message = f"Error while unlinking due to an exception: {e}" + + return Response(message=status_message, status=status) diff --git a/samples/apps/autogen-studio/autogenstudio/utils/utils.py b/samples/apps/autogen-studio/autogenstudio/utils/utils.py index 49a8ac91acd..0a5067f38b6 100644 --- a/samples/apps/autogen-studio/autogenstudio/utils/utils.py +++ b/samples/apps/autogen-studio/autogenstudio/utils/utils.py @@ -1,5 +1,6 @@ import base64 import hashlib +import json import os import re import shutil @@ -9,9 +10,10 @@ from dotenv import load_dotenv import autogen -from autogen.oai.client import OpenAIWrapper +from autogen.oai.client import ModelClient, OpenAIWrapper -from ..datamodel import AgentConfig, AgentFlowSpec, AgentWorkFlowConfig, LLMConfig, Model, Skill +from ..models.db import Agent, AgentType, Model, Skill, Workflow, WorkflowAgentLink +from ..models.dbmanager import DBManager from ..version import APP_NAME @@ -98,7 +100,16 @@ def get_file_type(file_path: str) -> str: CSV_EXTENSIONS = {".csv", ".xlsx"} # Supported image extensions - IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".svg", ".webp"} + IMAGE_EXTENSIONS = { + ".png", + ".jpg", + ".jpeg", + ".gif", + ".bmp", + ".tiff", + ".svg", + ".webp", + } # Supported (web) video extensions VIDEO_EXTENSIONS = {".mp4", ".webm", ".ogg", ".mov", ".avi", ".wmv"} @@ -258,11 +269,11 @@ def get_skills_from_prompt(skills: List[Skill], work_dir: str) -> str: for skill in skills: prompt += f""" -##### Begin of {skill.title} ##### +##### Begin of {skill.name} ##### {skill.content} -#### End of {skill.title} #### +#### End of {skill.name} #### """ @@ -309,55 +320,6 @@ def delete_files_in_folder(folders: Union[str, List[str]]) -> None: print(f"Failed to delete {path}. Reason: {e}") -def get_default_agent_config(work_dir: str) -> AgentWorkFlowConfig: - """ - Get a default agent flow config . - """ - - llm_config = LLMConfig( - config_list=[{"model": "gpt-4"}], - temperature=0, - ) - - USER_PROXY_INSTRUCTIONS = """If the request has been addressed sufficiently, summarize the answer and end with the word TERMINATE. Otherwise, ask a follow-up question. - """ - - userproxy_spec = AgentFlowSpec( - type="userproxy", - config=AgentConfig( - name="user_proxy", - human_input_mode="NEVER", - system_message=USER_PROXY_INSTRUCTIONS, - code_execution_config={ - "work_dir": work_dir, - "use_docker": False, - }, - max_consecutive_auto_reply=10, - llm_config=llm_config, - is_termination_msg=lambda x: x.get("content", "").rstrip().endswith("TERMINATE"), - ), - ) - - assistant_spec = AgentFlowSpec( - type="assistant", - config=AgentConfig( - name="primary_assistant", - system_message=autogen.AssistantAgent.DEFAULT_SYSTEM_MESSAGE, - llm_config=llm_config, - ), - ) - - flow_config = AgentWorkFlowConfig( - name="default", - sender=userproxy_spec, - receiver=assistant_spec, - type="default", - description="Default agent flow config", - ) - - return flow_config - - def extract_successful_code_blocks(messages: List[Dict[str, str]]) -> List[str]: """ Parses through a list of messages containing code blocks and execution statuses, @@ -392,7 +354,7 @@ def sanitize_model(model: Model): Sanitize model dictionary to remove None values and empty strings and only keep valid keys. """ if isinstance(model, Model): - model = model.dict() + model = model.model_dump() valid_keys = ["model", "base_url", "api_key", "api_type", "api_version"] # only add key if value is not None sanitized_model = {k: v for k, v in model.items() if (v is not None and v != "") and k in valid_keys} @@ -410,16 +372,61 @@ def test_model(model: Model): return response.choices[0].message.content -# summarize_chat_history (messages, model) .. returns a summary of the chat history - - -def summarize_chat_history(task: str, messages: List[Dict[str, str]], model: Model): +def workflow_from_id(workflow_id: int, dbmanager: DBManager): + workflow = dbmanager.get(Workflow, filters={"id": workflow_id}) + if not workflow or len(workflow) == 0: + return {} + workflow = workflow[0].model_dump(mode="json") + workflow_agent_links = dbmanager.get(WorkflowAgentLink, filters={"workflow_id": workflow_id}) + + def dump_agent(agent: Agent): + exclude = [] + if agent.type != AgentType.groupchat: + exclude = [ + "admin_name", + "messages", + "max_round", + "admin_name", + "speaker_selection_method", + "allow_repeat_speaker", + ] + return agent.model_dump(warnings=False, mode="json", exclude=exclude) + + def get_agent(agent_id): + agent: Agent = dbmanager.get(Agent, filters={"id": agent_id})[0] + agent_dict = dump_agent(agent) + agent_dict["skills"] = [Skill.model_validate(skill.model_dump(mode="json")) for skill in agent.skills] + model_exclude = [ + "id", + "agent_id", + "created_at", + "updated_at", + "user_id", + "description", + ] + models = [model.model_dump(mode="json", exclude=model_exclude) for model in agent.models] + agent_dict["models"] = [model.model_dump(mode="json") for model in agent.models] + + if len(models) > 0: + agent_dict["config"]["llm_config"] = agent_dict.get("config", {}).get("llm_config", {}) + llm_config = agent_dict["config"]["llm_config"] + if llm_config: + llm_config["config_list"] = models + agent_dict["config"]["llm_config"] = llm_config + agent_dict["agents"] = [get_agent(agent.id) for agent in agent.agents] + return agent_dict + + for link in workflow_agent_links: + agent_dict = get_agent(link.agent_id) + workflow[link.agent_type] = agent_dict + return workflow + + +def summarize_chat_history(task: str, messages: List[Dict[str, str]], client: ModelClient): """ Summarize the chat history using the model endpoint and returning the response. """ - sanitized_model = sanitize_model(model) - client = OpenAIWrapper(config_list=[sanitized_model]) summarization_system_prompt = f""" You are a helpful assistant that is able to review the chat history between a set of agents (userproxy agents, assistants etc) as they try to address a given TASK and provide a summary. Be SUCCINCT but also comprehensive enough to allow others (who cannot see the chat history) understand and recreate the solution. diff --git a/samples/apps/autogen-studio/autogenstudio/web/app.py b/samples/apps/autogen-studio/autogenstudio/web/app.py index 6d5412e9fed..171f583b04d 100644 --- a/samples/apps/autogen-studio/autogenstudio/web/app.py +++ b/samples/apps/autogen-studio/autogenstudio/web/app.py @@ -1,25 +1,22 @@ import asyncio -import json import os import queue import threading import traceback from contextlib import asynccontextmanager +from datetime import datetime +from typing import Any -from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect +from fastapi import FastAPI, WebSocket, WebSocketDisconnect from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from openai import OpenAIError from ..chatmanager import AutoGenChatManager, WebSocketConnectionManager -from ..datamodel import ( - DBWebRequestModel, - DeleteMessageWebRequestModel, - Message, - Session, -) -from ..utils import DBManager, dbutils, init_app_folders, md5_hash, test_model -from ..version import APP_NAME, VERSION +from ..models.db import Agent, Message, Model, Response, Session, Skill, Workflow +from ..models.dbmanager import DBManager +from ..utils import init_app_folders, md5_hash, test_model, workflow_from_id +from ..version import VERSION managers = {"chat": None} # manage calls to autogen # Create thread-safe queue for messages between api thread and autogen threads @@ -27,15 +24,18 @@ active_connections = [] active_connections_lock = asyncio.Lock() websocket_manager = WebSocketConnectionManager( - active_connections=active_connections, active_connections_lock=active_connections_lock + active_connections=active_connections, + active_connections_lock=active_connections_lock, ) def message_handler(): while True: message = message_queue.get() - print("Active Connections: ", [client_id for _, client_id in websocket_manager.active_connections]) - print("Current message connection id: ", message["connection_id"]) + print( + "Active Connections: ", + [client_id for _, client_id in websocket_manager.active_connections], + ) for connection, socket_client_id in websocket_manager.active_connections: if message["connection_id"] == socket_client_id: asyncio.run(websocket_manager.send_message(message, connection)) @@ -46,10 +46,20 @@ def message_handler(): message_handler_thread.start() +app_file_path = os.path.dirname(os.path.abspath(__file__)) +folders = init_app_folders(app_file_path) +ui_folder_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ui") + +db_path = os.path.join(folders["app_root"], "database.sqlite") +database_uri = os.environ.get("AUTOGENSTUDIO_DATABASE_URI", f"sqlite:///{db_path}") +dbmanager = DBManager(engine_uri=database_uri) + + @asynccontextmanager async def lifespan(app: FastAPI): print("***** App started *****") managers["chat"] = AutoGenChatManager(message_queue=message_queue) + dbmanager.create_db_and_tables() yield # Close all active connections @@ -75,477 +85,334 @@ async def lifespan(app: FastAPI): ) -app_file_path = os.path.dirname(os.path.abspath(__file__)) -# init folders skills, workdir, static, files etc -folders = init_app_folders(app_file_path) -ui_folder_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ui") - api = FastAPI(root_path="/api") # mount an api route such that the main route serves the ui and the /api app.mount("/api", api) app.mount("/", StaticFiles(directory=ui_folder_path, html=True), name="ui") -api.mount("/files", StaticFiles(directory=folders["files_static_root"], html=True), name="files") +api.mount( + "/files", + StaticFiles(directory=folders["files_static_root"], html=True), + name="files", +) -db_path = os.path.join(folders["app_root"], "database.sqlite") -dbmanager = DBManager(path=db_path) # manage database operations # manage websocket connections -@api.post("/messages") -async def add_message(req: DBWebRequestModel): - message = Message(**req.message.dict()) - user_history = dbutils.get_messages(user_id=message.user_id, session_id=req.message.session_id, dbmanager=dbmanager) +def check_and_cast_datetime_fields(obj: Any) -> Any: + if hasattr(obj, "created_at") and isinstance(obj.created_at, str): + obj.created_at = str_to_datetime(obj.created_at) - # save incoming message to db - dbutils.create_message(message=message, dbmanager=dbmanager) - user_dir = os.path.join(folders["files_static_root"], "user", md5_hash(message.user_id)) - os.makedirs(user_dir, exist_ok=True) + if hasattr(obj, "updated_at") and isinstance(obj.updated_at, str): + obj.updated_at = str_to_datetime(obj.updated_at) - try: - response_message: Message = managers["chat"].chat( - message=message, - history=user_history, - user_dir=user_dir, - flow_config=req.workflow, - connection_id=req.connection_id, - ) + return obj - # save agent's response to db - messages = dbutils.create_message(message=response_message, dbmanager=dbmanager) - response = { - "status": True, - "message": "Message processed successfully", - "data": messages, - # "metadata": json.loads(response_message.metadata), - } - return response - except Exception as ex_error: - print(traceback.format_exc()) - return { - "status": False, - "message": "Error occurred while processing message: " + str(ex_error), - } +def str_to_datetime(dt_str: str) -> datetime: + if dt_str[-1] == "Z": + # Replace 'Z' with '+00:00' for UTC timezone + dt_str = dt_str[:-1] + "+00:00" + return datetime.fromisoformat(dt_str) -@api.get("/messages") -async def get_messages(user_id: str = None, session_id: str = None): - if user_id is None: - raise HTTPException(status_code=400, detail="user_id is required") - try: - user_history = dbutils.get_messages(user_id=user_id, session_id=session_id, dbmanager=dbmanager) - - return { - "status": True, - "data": user_history, - "message": "Messages retrieved successfully", - } - except Exception as ex_error: - print(ex_error) - return { - "status": False, - "message": "Error occurred while retrieving messages: " + str(ex_error), - } - - -@api.get("/gallery") -async def get_gallery_items(gallery_id: str = None): - try: - gallery = dbutils.get_gallery(gallery_id=gallery_id, dbmanager=dbmanager) - return { - "status": True, - "data": gallery, - "message": "Gallery items retrieved successfully", - } - except Exception as ex_error: - print(ex_error) - return { - "status": False, - "message": "Error occurred while retrieving messages: " + str(ex_error), - } - - -@api.get("/sessions") -async def get_user_sessions(user_id: str = None): - """Return a list of all sessions for a user""" - if user_id is None: - raise HTTPException(status_code=400, detail="user_id is required") +def create_entity(model: Any, model_class: Any, filters: dict = None): + """Create a new entity""" + model = check_and_cast_datetime_fields(model) try: - user_sessions = dbutils.get_sessions(user_id=user_id, dbmanager=dbmanager) + response: Response = dbmanager.upsert(model) + return response.model_dump(mode="json") - return { - "status": True, - "data": user_sessions, - "message": "Sessions retrieved successfully", - } except Exception as ex_error: print(ex_error) return { "status": False, - "message": "Error occurred while retrieving sessions: " + str(ex_error), + "message": f"Error occurred while creating {model_class.__name__}: " + str(ex_error), } -@api.post("/sessions") -async def create_user_session(req: DBWebRequestModel): - """Create a new session for a user""" - # print(req.session, "**********" ) - +def list_entity( + model_class: Any, + filters: dict = None, + return_json: bool = True, + order: str = "desc", +): + """List all entities for a user""" try: - session = Session(user_id=req.session.user_id, flow_config=req.session.flow_config) - user_sessions = dbutils.create_session(user_id=req.user_id, session=session, dbmanager=dbmanager) + entities = dbmanager.get(model_class, filters=filters, return_json=return_json, order=order) return { "status": True, - "message": "Session created successfully", - "data": user_sessions, - } - except Exception as ex_error: - print(traceback.format_exc()) - return { - "status": False, - "message": "Error occurred while creating session: " + str(ex_error), + "message": f"{model_class.__name__} retrieved successfully", + "data": entities, } - -@api.post("/sessions/rename") -async def rename_user_session(name: str, req: DBWebRequestModel): - """Rename a session for a user""" - print("Rename: " + name) - print("renaming session for user: " + req.user_id + " to: " + name) - try: - session = dbutils.rename_session(name=name, session=req.session, dbmanager=dbmanager) - return { - "status": True, - "message": "Session renamed successfully", - "data": session, - } except Exception as ex_error: - print(traceback.format_exc()) + print(ex_error) return { "status": False, - "message": "Error occurred while renaming session: " + str(ex_error), + "message": f"Error occurred while retrieving {model_class.__name__}: " + str(ex_error), } -@api.post("/sessions/publish") -async def publish_user_session_to_gallery(req: DBWebRequestModel): - """Create a new session for a user""" +def delete_entity(model_class: Any, filters: dict = None): + """Delete an entity""" - try: - gallery_item = dbutils.create_gallery(req.session, tags=req.tags, dbmanager=dbmanager) - return { - "status": True, - "message": "Session successfully published", - "data": gallery_item, - } - except Exception as ex_error: - print(traceback.format_exc()) - return { - "status": False, - "message": "Error occurred while publishing session: " + str(ex_error), - } + return dbmanager.delete(filters=filters, model_class=model_class) -@api.delete("/sessions/delete") -async def delete_user_session(req: DBWebRequestModel): - """Delete a session for a user""" +@api.get("/skills") +async def list_skills(user_id: str): + """List all skills for a user""" + filters = {"user_id": user_id} + return list_entity(Skill, filters=filters) - try: - sessions = dbutils.delete_session(session=req.session, dbmanager=dbmanager) - return { - "status": True, - "message": "Session deleted successfully", - "data": sessions, - } - except Exception as ex_error: - print(traceback.format_exc()) - return { - "status": False, - "message": "Error occurred while deleting session: " + str(ex_error), - } +@api.post("/skills") +async def create_skill(skill: Skill): + """Create a new skill""" + filters = {"user_id": skill.user_id} + return create_entity(skill, Skill, filters=filters) -@api.post("/messages/delete") -async def remove_message(req: DeleteMessageWebRequestModel): - """Delete a message from the database""" - try: - messages = dbutils.delete_message( - user_id=req.user_id, msg_id=req.msg_id, session_id=req.session_id, dbmanager=dbmanager - ) - return { - "status": True, - "message": "Message deleted successfully", - "data": messages, - } - except Exception as ex_error: - print(ex_error) - return { - "status": False, - "message": "Error occurred while deleting message: " + str(ex_error), - } +@api.delete("/skills/delete") +async def delete_skill(skill_id: int, user_id: str): + """Delete a skill""" + filters = {"id": skill_id, "user_id": user_id} + return delete_entity(Skill, filters=filters) -@api.get("/skills") -async def get_user_skills(user_id: str): - try: - skills = dbutils.get_skills(user_id, dbmanager=dbmanager) +@api.get("/models") +async def list_models(user_id: str): + """List all models for a user""" + filters = {"user_id": user_id} + return list_entity(Model, filters=filters) - return { - "status": True, - "message": "Skills retrieved successfully", - "data": skills, - } - except Exception as ex_error: - print(ex_error) - return { - "status": False, - "message": "Error occurred while retrieving skills: " + str(ex_error), - } +@api.post("/models") +async def create_model(model: Model): + """Create a new model""" + return create_entity(model, Model) -@api.post("/skills") -async def create_user_skills(req: DBWebRequestModel): + +@api.post("/models/test") +async def test_model_endpoint(model: Model): + """Test a model""" try: - skills = dbutils.upsert_skill(skill=req.skill, dbmanager=dbmanager) + response = test_model(model) return { "status": True, - "message": "Skills retrieved successfully", - "data": skills, + "message": "Model tested successfully", + "data": response, } - - except Exception as ex_error: - print(ex_error) + except (OpenAIError, Exception) as ex_error: return { "status": False, - "message": "Error occurred while creating skills: " + str(ex_error), + "message": "Error occurred while testing model: " + str(ex_error), } -@api.delete("/skills/delete") -async def delete_user_skills(req: DBWebRequestModel): - """Delete a skill for a user""" +@api.delete("/models/delete") +async def delete_model(model_id: int, user_id: str): + """Delete a model""" + filters = {"id": model_id, "user_id": user_id} + return delete_entity(Model, filters=filters) - try: - skills = dbutils.delete_skill(req.skill, dbmanager=dbmanager) - return { - "status": True, - "message": "Skill deleted successfully", - "data": skills, - } +@api.get("/agents") +async def list_agents(user_id: str): + """List all agents for a user""" + filters = {"user_id": user_id} + return list_entity(Agent, filters=filters) - except Exception as ex_error: - print(ex_error) - return { - "status": False, - "message": "Error occurred while deleting skill: " + str(ex_error), - } +@api.post("/agents") +async def create_agent(agent: Agent): + """Create a new agent""" + return create_entity(agent, Agent) -@api.get("/agents") -async def get_user_agents(user_id: str): - try: - agents = dbutils.get_agents(user_id, dbmanager=dbmanager) - return { - "status": True, - "message": "Agents retrieved successfully", - "data": agents, - } - except Exception as ex_error: - print(ex_error) - return { - "status": False, - "message": "Error occurred while retrieving agents: " + str(ex_error), - } +@api.delete("/agents/delete") +async def delete_agent(agent_id: int, user_id: str): + """Delete an agent""" + filters = {"id": agent_id, "user_id": user_id} + return delete_entity(Agent, filters=filters) -@api.post("/agents") -async def create_user_agents(req: DBWebRequestModel): - """Create a new agent for a user""" +@api.post("/agents/link/model/{agent_id}/{model_id}") +async def link_agent_model(agent_id: int, model_id: int): + """Link a model to an agent""" + return dbmanager.link(link_type="agent_model", primary_id=agent_id, secondary_id=model_id) - try: - agents = dbutils.upsert_agent(agent_flow_spec=req.agent, dbmanager=dbmanager) - return { - "status": True, - "message": "Agent created successfully", - "data": agents, - } +@api.delete("/agents/link/model/{agent_id}/{model_id}") +async def unlink_agent_model(agent_id: int, model_id: int): + """Unlink a model from an agent""" + return dbmanager.unlink(link_type="agent_model", primary_id=agent_id, secondary_id=model_id) - except Exception as ex_error: - print(traceback.format_exc()) - return { - "status": False, - "message": "Error occurred while creating agent: " + str(ex_error), - } +@api.get("/agents/link/model/{agent_id}") +async def get_agent_models(agent_id: int): + """Get all models linked to an agent""" + return dbmanager.get_linked_entities("agent_model", agent_id, return_json=True) -@api.delete("/agents/delete") -async def delete_user_agent(req: DBWebRequestModel): - """Delete an agent for a user""" - try: - agents = dbutils.delete_agent(agent=req.agent, dbmanager=dbmanager) +@api.post("/agents/link/skill/{agent_id}/{skill_id}") +async def link_agent_skill(agent_id: int, skill_id: int): + """Link an a skill to an agent""" + return dbmanager.link(link_type="agent_skill", primary_id=agent_id, secondary_id=skill_id) - return { - "status": True, - "message": "Agent deleted successfully", - "data": agents, - } - except Exception as ex_error: - print(traceback.format_exc()) - return { - "status": False, - "message": "Error occurred while deleting agent: " + str(ex_error), - } +@api.delete("/agents/link/skill/{agent_id}/{skill_id}") +async def unlink_agent_skill(agent_id: int, skill_id: int): + """Unlink an a skill from an agent""" + return dbmanager.unlink(link_type="agent_skill", primary_id=agent_id, secondary_id=skill_id) -@api.get("/models") -async def get_user_models(user_id: str): - try: - models = dbutils.get_models(user_id, dbmanager=dbmanager) +@api.get("/agents/link/skill/{agent_id}") +async def get_agent_skills(agent_id: int): + """Get all skills linked to an agent""" + return dbmanager.get_linked_entities("agent_skill", agent_id, return_json=True) - return { - "status": True, - "message": "Models retrieved successfully", - "data": models, - } - except Exception as ex_error: - print(ex_error) - return { - "status": False, - "message": "Error occurred while retrieving models: " + str(ex_error), - } +@api.post("/agents/link/agent/{primary_agent_id}/{secondary_agent_id}") +async def link_agent_agent(primary_agent_id: int, secondary_agent_id: int): + """Link an agent to another agent""" + return dbmanager.link( + link_type="agent_agent", + primary_id=primary_agent_id, + secondary_id=secondary_agent_id, + ) -@api.post("/models") -async def create_user_models(req: DBWebRequestModel): - """Create a new model for a user""" - try: - models = dbutils.upsert_model(model=req.model, dbmanager=dbmanager) +@api.delete("/agents/link/agent/{primary_agent_id}/{secondary_agent_id}") +async def unlink_agent_agent(primary_agent_id: int, secondary_agent_id: int): + """Unlink an agent from another agent""" + return dbmanager.unlink( + link_type="agent_agent", + primary_id=primary_agent_id, + secondary_id=secondary_agent_id, + ) - return { - "status": True, - "message": "Model created successfully", - "data": models, - } - except Exception as ex_error: - print(traceback.format_exc()) - return { - "status": False, - "message": "Error occurred while creating model: " + str(ex_error), - } +@api.get("/agents/link/agent/{agent_id}") +async def get_linked_agents(agent_id: int): + """Get all agents linked to an agent""" + return dbmanager.get_linked_entities("agent_agent", agent_id, return_json=True) -@api.post("/models/test") -async def test_user_models(req: DBWebRequestModel): - """Test a model to verify it works""" +@api.get("/workflows") +async def list_workflows(user_id: str): + """List all workflows for a user""" + filters = {"user_id": user_id} + return list_entity(Workflow, filters=filters) - try: - response = test_model(model=req.model) - return { - "status": True, - "message": "Model tested successfully", - "data": response, - } - except OpenAIError as oai_error: - print(traceback.format_exc()) - return { - "status": False, - "message": "Error occurred while testing model: " + str(oai_error), - } - except Exception as ex_error: - print(traceback.format_exc()) - return { - "status": False, - "message": "Error occurred while testing model: " + str(ex_error), - } +@api.post("/workflows") +async def create_workflow(workflow: Workflow): + """Create a new workflow""" + return create_entity(workflow, Workflow) -@api.delete("/models/delete") -async def delete_user_model(req: DBWebRequestModel): - """Delete a model for a user""" +@api.delete("/workflows/delete") +async def delete_workflow(workflow_id: int, user_id: str): + """Delete a workflow""" + filters = {"id": workflow_id, "user_id": user_id} + return delete_entity(Workflow, filters=filters) + + +@api.post("/workflows/link/agent/{workflow_id}/{agent_id}/{agent_type}") +async def link_workflow_agent(workflow_id: int, agent_id: int, agent_type: str): + """Link an agent to a workflow""" + return dbmanager.link( + link_type="workflow_agent", + primary_id=workflow_id, + secondary_id=agent_id, + agent_type=agent_type, + ) + + +@api.delete("/workflows/link/agent/{workflow_id}/{agent_id}/{agent_type}") +async def unlink_workflow_agent(workflow_id: int, agent_id: int, agent_type: str): + """Unlink an agent from a workflow""" + return dbmanager.unlink( + link_type="workflow_agent", + primary_id=workflow_id, + secondary_id=agent_id, + agent_type=agent_type, + ) + + +@api.get("/workflows/link/agent/{workflow_id}/{agent_type}") +async def get_linked_workflow_agents(workflow_id: int, agent_type: str): + """Get all agents linked to a workflow""" + return dbmanager.get_linked_entities( + link_type="workflow_agent", + primary_id=workflow_id, + agent_type=agent_type, + return_json=True, + ) - try: - models = dbutils.delete_model(model=req.model, dbmanager=dbmanager) - return { - "status": True, - "message": "Model deleted successfully", - "data": models, - } +@api.get("/sessions") +async def list_sessions(user_id: str): + """List all sessions for a user""" + filters = {"user_id": user_id} + return list_entity(Session, filters=filters) - except Exception as ex_error: - print(traceback.format_exc()) - return { - "status": False, - "message": "Error occurred while deleting model: " + str(ex_error), - } +@api.post("/sessions") +async def create_session(session: Session): + """Create a new session""" + return create_entity(session, Session) -@api.get("/workflows") -async def get_user_workflows(user_id: str): - try: - workflows = dbutils.get_workflows(user_id, dbmanager=dbmanager) - return { - "status": True, - "message": "Workflows retrieved successfully", - "data": workflows, - } - except Exception as ex_error: - print(ex_error) - return { - "status": False, - "message": "Error occurred while retrieving workflows: " + str(ex_error), - } +@api.delete("/sessions/delete") +async def delete_session(session_id: int, user_id: str): + """Delete a session""" + filters = {"id": session_id, "user_id": user_id} + return delete_entity(Session, filters=filters) -@api.post("/workflows") -async def create_user_workflow(req: DBWebRequestModel): - """Create a new workflow for a user""" - try: - workflow = dbutils.upsert_workflow(workflow=req.workflow, dbmanager=dbmanager) - return { - "status": True, - "message": "Workflow created successfully", - "data": workflow, - } +@api.get("/messages") +async def list_messages(user_id: str, session_id: str): + """List all messages for a user""" + filters = {"user_id": user_id, "session_id": session_id} + return list_entity(Message, filters=filters, order="asc", return_json=True) - except Exception as ex_error: - print(ex_error) - return { - "status": False, - "message": "Error occurred while creating workflow: " + str(ex_error), - } +@api.post("/messages") +async def create_message(message: Message): + """Create a new message""" -@api.delete("/workflows/delete") -async def delete_user_workflow(req: DBWebRequestModel): - """Delete a workflow for a user""" + user_message_history = dbmanager.get( + Message, + filters={"user_id": message.user_id, "session_id": message.session_id}, + return_json=True, + ) + # save incoming message + dbmanager.upsert(message) + user_dir = os.path.join(folders["files_static_root"], "user", md5_hash(message.user_id)) + os.makedirs(user_dir, exist_ok=True) + workflow = workflow_from_id(message.workflow_id, dbmanager=dbmanager) try: - workflow = dbutils.delete_workflow(workflow=req.workflow, dbmanager=dbmanager) - return { - "status": True, - "message": "Workflow deleted successfully", - "data": workflow, - } + agent_response: Message = managers["chat"].chat( + message=message, + history=user_message_history, + user_dir=user_dir, + workflow=workflow, + connection_id=message.connection_id, + ) + response: Response = dbmanager.upsert(agent_response) + return response.model_dump(mode="json") except Exception as ex_error: - print(ex_error) + print(traceback.format_exc()) return { "status": False, - "message": "Error occurred while deleting workflow: " + str(ex_error), + "message": "Error occurred while processing message: " + str(ex_error), } @@ -558,11 +425,14 @@ async def get_version(): } +# websockets + + async def process_socket_message(data: dict, websocket: WebSocket, client_id: str): print(f"Client says: {data['type']}") if data["type"] == "user_message": - user_request_body = DBWebRequestModel(**data["data"]) - response = await add_message(user_request_body) + user_message = Message(**data["data"]) + response = await create_message(user_message) response_socket_message = { "type": "agent_response", "data": response, diff --git a/samples/apps/autogen-studio/autogenstudio/workflowmanager.py b/samples/apps/autogen-studio/autogenstudio/workflowmanager.py index c5475e58d83..82872b099ee 100644 --- a/samples/apps/autogen-studio/autogenstudio/workflowmanager.py +++ b/samples/apps/autogen-studio/autogenstudio/workflowmanager.py @@ -1,23 +1,25 @@ import os from datetime import datetime -from typing import Dict, List, Optional, Union - -from requests import Session +from typing import Any, Dict, List, Optional, Union import autogen - -from .datamodel import AgentConfig, AgentFlowSpec, AgentWorkFlowConfig, Message, SocketMessage -from .utils import clear_folder, get_skills_from_prompt, sanitize_model +from autogenstudio.models.db import ( + Agent, + AgentType, + Message, + SocketMessage, +) +from autogenstudio.utils import clear_folder, get_skills_from_prompt, sanitize_model -class AutoGenWorkFlowManager: +class WorkflowManager: """ AutoGenWorkFlowManager class to load agents from a provided configuration and run a chat between them """ def __init__( self, - config: AgentWorkFlowConfig, + workflow: Dict, history: Optional[List[Message]] = None, work_dir: str = None, clear_work_dir: bool = True, @@ -38,15 +40,51 @@ def __init__( self.work_dir = work_dir or "work_dir" if clear_work_dir: clear_folder(self.work_dir) - self.config = config - # given the config, return an AutoGen agent object - self.sender = self.load(config.sender) - # given the config, return an AutoGen agent object - self.receiver = self.load(config.receiver) + self.workflow = workflow + self.sender = self.load(workflow.get("sender")) + self.receiver = self.load(workflow.get("receiver")) self.agent_history = [] if history: - self.populate_history(history) + self._populate_history(history) + + def _serialize_agent( + self, + agent: Agent, + mode: str = "python", + include: Optional[List[str]] = {"config"}, + exclude: Optional[List[str]] = None, + ) -> Dict: + """ """ + # exclude = ["id","created_at", "updated_at","user_id","type"] + exclude = exclude or {} + include = include or {} + if agent.type != AgentType.groupchat: + exclude.update( + { + "config": { + "admin_name", + "messages", + "max_round", + "admin_name", + "speaker_selection_method", + "allow_repeat_speaker", + } + } + ) + else: + include = { + "config": { + "admin_name", + "messages", + "max_round", + "admin_name", + "speaker_selection_method", + "allow_repeat_speaker", + } + } + result = agent.model_dump(warnings=False, exclude=exclude, include=include, mode=mode) + return result["config"] def process_message( self, @@ -84,25 +122,14 @@ def process_message( if request_reply is not False or sender_type == "groupchat": self.agent_history.append(message_payload) # add to history if self.send_message_function: # send over the message queue - socket_msg = SocketMessage(type="agent_message", data=message_payload, connection_id=self.connection_id) + socket_msg = SocketMessage( + type="agent_message", + data=message_payload, + connection_id=self.connection_id, + ) self.send_message_function(socket_msg.dict()) - def _sanitize_history_message(self, message: str) -> str: - """ - Sanitizes the message e.g. remove references to execution completed - - Args: - message: The message to be sanitized. - - Returns: - The sanitized message. - """ - to_replace = ["execution succeeded", "exitcode"] - for replace in to_replace: - message = message.replace(replace, "") - return message - - def populate_history(self, history: List[Message]) -> None: + def _populate_history(self, history: List[Message]) -> None: """ Populates the agent message history from the provided list of messages. @@ -127,19 +154,12 @@ def populate_history(self, history: List[Message]) -> None: silent=True, ) - def sanitize_agent_spec(self, agent_spec: AgentFlowSpec) -> AgentFlowSpec: - """ - Sanitizes the agent spec by setting loading defaults - - Args: - config: The agent configuration to be sanitized. - agent_type: The type of the agent. + def sanitize_agent(self, agent: Dict) -> Agent: + """ """ - Returns: - The sanitized agent configuration. - """ - - agent_spec.config.is_termination_msg = agent_spec.config.is_termination_msg or ( + skills = agent.get("skills", []) + agent = Agent.model_validate(agent) + agent.config.is_termination_msg = agent.config.is_termination_msg or ( lambda x: "TERMINATE" in x.get("content", "").rstrip()[-20:] ) @@ -149,40 +169,36 @@ def get_default_system_message(agent_type: str) -> str: else: return "You are a helpful AI Assistant." - # sanitize llm_config if present - if agent_spec.config.llm_config is not False: + if agent.config.llm_config is not False: config_list = [] - for llm in agent_spec.config.llm_config.config_list: + for llm in agent.config.llm_config.config_list: # check if api_key is present either in llm or env variable if "api_key" not in llm and "OPENAI_API_KEY" not in os.environ: - error_message = f"api_key is not present in llm_config or OPENAI_API_KEY env variable for agent ** {agent_spec.config.name}**. Update your workflow to provide an api_key to use the LLM." + error_message = f"api_key is not present in llm_config or OPENAI_API_KEY env variable for agent ** {agent.config.name}**. Update your workflow to provide an api_key to use the LLM." raise ValueError(error_message) # only add key if value is not None sanitized_llm = sanitize_model(llm) config_list.append(sanitized_llm) - agent_spec.config.llm_config.config_list = config_list - if agent_spec.config.code_execution_config is not False: - code_execution_config = agent_spec.config.code_execution_config or {} + print("llm config >> ", sanitized_llm) + agent.config.llm_config.config_list = config_list + if agent.config.code_execution_config is not False: + code_execution_config = agent.config.code_execution_config or {} code_execution_config["work_dir"] = self.work_dir # tbd check if docker is installed code_execution_config["use_docker"] = False - agent_spec.config.code_execution_config = code_execution_config - - if agent_spec.skills: - # get skill prompt, also write skills to a file named skills.py - skills_prompt = "" - skills_prompt = get_skills_from_prompt(agent_spec.skills, self.work_dir) - if agent_spec.config.system_message: - agent_spec.config.system_message = agent_spec.config.system_message + "\n\n" + skills_prompt - else: - agent_spec.config.system_message = ( - get_default_system_message(agent_spec.type) + "\n\n" + skills_prompt - ) - - return agent_spec - - def load(self, agent_spec: AgentFlowSpec) -> autogen.Agent: + agent.config.code_execution_config = code_execution_config + + if skills: + skills_prompt = "" + skills_prompt = get_skills_from_prompt(skills, self.work_dir) + if agent.config.system_message: + agent.config.system_message = agent.config.system_message + "\n\n" + skills_prompt + else: + agent.config.system_message = get_default_system_message(agent.type) + "\n\n" + skills_prompt + return agent + + def load(self, agent: Any) -> autogen.Agent: """ Loads an agent based on the provided agent specification. @@ -192,43 +208,40 @@ def load(self, agent_spec: AgentFlowSpec) -> autogen.Agent: Returns: An instance of the loaded agent. """ - agent_spec = self.sanitize_agent_spec(agent_spec) - if agent_spec.type == "groupchat": - agents = [ - self.load(self.sanitize_agent_spec(agent_config)) for agent_config in agent_spec.groupchat_config.agents - ] - group_chat_config = agent_spec.groupchat_config.dict() - group_chat_config["agents"] = agents + if not agent: + raise ValueError( + "An agent configuration in this workflow is empty. Please provide a valid agent configuration." + ) + + linked_agents = agent.get("agents", []) + agent = self.sanitize_agent(agent) + if agent.type == "groupchat": + groupchat_agents = [self.load(agent) for agent in linked_agents] + group_chat_config = self._serialize_agent(agent) + group_chat_config["agents"] = groupchat_agents groupchat = autogen.GroupChat(**group_chat_config) agent = ExtendedGroupChatManager( - groupchat=groupchat, **agent_spec.config.dict(), message_processor=self.process_message + groupchat=groupchat, + message_processor=self.process_message, + llm_config=agent.config.llm_config.model_dump(), ) return agent else: - agent = self.load_agent_config(agent_spec.config, agent_spec.type) + if agent.type == "assistant": + agent = ExtendedConversableAgent( + **self._serialize_agent(agent), + message_processor=self.process_message, + ) + elif agent.type == "userproxy": + agent = ExtendedConversableAgent( + **self._serialize_agent(agent), + message_processor=self.process_message, + ) + else: + raise ValueError(f"Unknown agent type: {agent.type}") return agent - def load_agent_config(self, agent_config: AgentConfig, agent_type: str) -> autogen.Agent: - """ - Loads an agent based on the provided agent configuration. - - Args: - agent_config: The configuration of the agent to be loaded. - agent_type: The type of the agent to be loaded. - - Returns: - An instance of the loaded agent. - """ - if agent_type == "assistant": - agent = ExtendedConversableAgent(**agent_config.dict(), message_processor=self.process_message) - elif agent_type == "userproxy": - agent = ExtendedConversableAgent(**agent_config.dict(), message_processor=self.process_message) - else: - raise ValueError(f"Unknown agent type: {agent_type}") - - return agent - def run(self, message: str, clear_history: bool = False) -> None: """ Initiates a chat between the sender and receiver agents with an initial message @@ -262,6 +275,9 @@ def receive( super().receive(message, sender, request_reply, silent) +"" + + class ExtendedGroupChatManager(autogen.GroupChatManager): def __init__(self, message_processor=None, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/samples/apps/autogen-studio/docs/ara_stockprices.png b/samples/apps/autogen-studio/docs/ara_stockprices.png index f5adf6256e554f6283590a4eeff2812dfeec2d85..fafb830ef1a121eec1a9ebc488b56c01c233b22a 100644 GIT binary patch literal 198222 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∋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 z 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

k^{$;x-L%_qKraKp8Qjzv?<;fpl(b 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

    NhS=>WxZ~`#2Q9A)F{E&4}O{a?xuDn#Nd=& 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

    Re@Z=bqe5a~yLVTLtw|+kP5Sm%mnZ^xmPK2S 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 literal 131 zcmWN?!4bkB5CFhGRnUL|jyoXf1~?FAR5F5jSiSCNukxOKykuMJoQG2PzHW~?xBu;v zwmhC{o+Ycxh|yWj7J=MB#ah(SJL2fLR!4zyNKisVZIG+ZJ0`N89k4~iz?zX9_>{6m N60?7cG

    {title && ( @@ -176,7 +142,7 @@ export const Card = ({
    {subtitle}
    {children}
    - + ); }; @@ -303,7 +269,7 @@ export const MessageBox = ({ title, children, className }: IProps) => { export const GroupView = ({ children, title, - className = " bg-primary ", + className = "text-primary bg-primary ", }: any) => { return (
    @@ -593,7 +559,7 @@ export const ControlRowView = ({ }: { title: string; description: string; - value: string | number; + value: string | number | boolean; control: any; className?: string; }) => { @@ -614,291 +580,6 @@ export const ControlRowView = ({ ); }; -export const ModelSelector = ({ - configs, - setConfigs, - className, -}: { - configs: IModelConfig[]; - setConfigs: (configs: IModelConfig[]) => void; - className?: string; -}) => { - // const [configs, setConfigs] = useState(modelConfigs); - const [isModalVisible, setIsModalVisible] = useState(false); - const [newModelConfig, setNewModelConfig] = useState( - null - ); - const [editIndex, setEditIndex] = useState(null); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(null); - - const [models, setModels] = useState([]); - const serverUrl = getServerUrl(); - - const { user } = React.useContext(appContext); - const listModelsUrl = `${serverUrl}/models?user_id=${user?.email}`; - - // const sanitizeModelConfig = (config: IModelConfig) => { - // const sanitizedConfig: IModelConfig = { model: config.model }; - // if (config.api_key) sanitizedConfig.api_key = config.api_key; - // if (config.base_url) sanitizedConfig.base_url = config.base_url; - // if (config.api_type) sanitizedConfig.api_type = config.api_type; - // if (config.api_version) sanitizedConfig.api_version = config.api_version; - // return sanitizedConfig; - // }; - - const handleRemoveConfig = (index: number) => { - const updatedConfigs = configs.filter((_, i) => i !== index); - - setConfigs(updatedConfigs); - }; - - const showModal = (config: IModelConfig | null, index: number | null) => { - setNewModelConfig(config); - setEditIndex(index); - setIsModalVisible(true); - }; - - const fetchModels = () => { - setError(null); - setLoading(true); - // const fetch; - const payLoad = { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }; - - const onSuccess = (data: any) => { - if (data && data.status) { - // message.success(data.message); - setModels(data.data); - } else { - message.error(data.message); - } - setLoading(false); - }; - const onError = (err: any) => { - setError(err); - message.error(err.message); - setLoading(false); - }; - fetchJSON(listModelsUrl, payLoad, onSuccess, onError); - }; - - useEffect(() => { - fetchModels(); - }, []); - - const modelItems: MenuProps["items"] = - models.length > 0 - ? models.map((model: IModelConfig, index: number) => ({ - key: index, - label: ( - <> -
    {model.model}
    -
    - {truncateText(model.description || "", 20)} -
    - - ), - value: index, - })) - : [ - { - key: -1, - label: <>No models found, - value: 0, - }, - ]; - - const modelOnClick: MenuProps["onClick"] = ({ key }) => { - const selectedIndex = parseInt(key.toString()); - let selectedModel = models[selectedIndex]; - const updatedConfigs = [...configs, selectedModel]; - setConfigs(updatedConfigs); - }; - - const menuStyle: React.CSSProperties = { - boxShadow: "none", - }; - - const { token } = useToken(); - const contentStyle: React.CSSProperties = { - backgroundColor: token.colorBgElevated, - borderRadius: token.borderRadiusLG, - boxShadow: token.boxShadowSecondary, - }; - - const addModelsMessage = ( - - {" "} - Please - create models in the Model tab - - ); - - const AddModelsDropDown = () => { - return ( - ( -
    - {React.cloneElement(menu as React.ReactElement, { - style: menuStyle, - })} - {models.length === 0 && ( - <> - - -
    {addModelsMessage}
    - - )} -
    - )} - > -
    - add -
    -
    - ); - }; - - const handleOk = () => { - if (newModelConfig?.model.trim()) { - const sanitizedConfig = newModelConfig; - - if (editIndex !== null) { - // Edit existing model - const updatedConfigs = [...configs]; - updatedConfigs[editIndex] = sanitizedConfig; - setConfigs(updatedConfigs); - } else { - // Add new model - setConfigs([...configs, sanitizedConfig]); - } - setIsModalVisible(false); - setNewModelConfig(null); - setEditIndex(null); - } else { - // Handle case where 'model' field is empty - // Could provide user feedback here (e.g., input validation error) - message.error("Model name cannot be empty"); - } - }; - - const handleCancel = () => { - setIsModalVisible(false); - setNewModelConfig(null); - setEditIndex(null); - }; - - const updateNewModelConfig = (field: keyof IModelConfig, value: string) => { - setNewModelConfig((prevState) => - prevState ? { ...prevState, [field]: value } : null - ); - }; - - const modelButtons = configs.map((config, i) => { - const tooltipText = ( - <> -
    {config.model}
    - {config.base_url &&
    {config.base_url}
    } - {config.api_key &&
    {obscureString(config.api_key, 3)}
    } -
    - {truncateText(config.description || "", 90)} -
    - - ); - return ( -
    showModal(config, i)} - > -
    - {" "} - -
    {config.model}
    {" "} -
    -
    { - e.stopPropagation(); // Prevent opening the modal to edit - handleRemoveConfig(i); - }} - className="ml-1 text-primary hover:text-accent duration-300" - > - -
    -
    -
    - ); - }); - - return ( -
    -
    - {modelButtons} - -
    - - Cancel - , - , - ]} - > -
    Enter parameters for your model.
    - updateNewModelConfig("model", e.target.value)} - /> - updateNewModelConfig("api_key", e.target.value)} - /> - updateNewModelConfig("base_url", e.target.value)} - /> - updateNewModelConfig("api_type", e.target.value)} - /> - - updateNewModelConfig("api_version", e.target.value)} - /> -
    -
    - ); -}; - export const BounceLoader = ({ className, title = "", @@ -937,7 +618,7 @@ export const ImageLoader = ({ Dynamic content setIsLoading(false)} @@ -1077,946 +758,6 @@ export const PdfViewer = ({ url }: { url: string }) => { ); }; -export const AgentFlowSpecView = ({ - title = "Agent Specification", - flowSpec, - setFlowSpec, -}: { - title: string; - flowSpec: IAgentFlowSpec; - setFlowSpec: (newFlowSpec: IAgentFlowSpec) => void; - editMode?: boolean; -}) => { - // Local state for the FlowView component - const [localFlowSpec, setLocalFlowSpec] = - React.useState(flowSpec); - - // Required to monitor localAgent updates that occur in GroupChatFlowSpecView and reflect updates. - useEffect(() => { - setLocalFlowSpec(flowSpec); - }, [flowSpec]); - - // Event handlers for updating local state and propagating changes - - const onControlChange = (value: any, key: string) => { - if (key === "llm_config") { - if (value.config_list.length === 0) { - value = false; - } - } - const updatedFlowSpec = { - ...localFlowSpec, - config: { ...localFlowSpec.config, [key]: value }, - }; - - setLocalFlowSpec(updatedFlowSpec); - setFlowSpec(updatedFlowSpec); - }; - - const llm_config: ILLMConfig = localFlowSpec?.config?.llm_config || { - config_list: [], - temperature: 0.1, - }; - - const nameValidation = checkAndSanitizeInput(flowSpec?.config?.name); - - return ( - <> -
    {title}
    - {flowSpec?.config?.name}
    - className="mb-4 bg-primary " - > - - { - onControlChange(e.target.value, "name"); - }} - /> - {!nameValidation.status && ( -
    - {nameValidation.message} -
    - )} - - } - /> - - { - onControlChange(e.target.value, "description"); - }} - /> - } - /> - - { - onControlChange(value, "max_consecutive_auto_reply"); - }} - /> - } - /> - - { - onControlChange(e.target.value, "default_auto_reply"); - }} - /> - } - /> - - { - onControlChange(value, "human_input_mode"); - }} - options={ - [ - { label: "NEVER", value: "NEVER" }, - // { label: "TERMINATE", value: "TERMINATE" }, - // { label: "ALWAYS", value: "ALWAYS" }, - ] as any - } - /> - } - /> - - {llm_config && llm_config.config_list.length > 0 && ( - { - onControlChange(e.target.value, "system_message"); - }} - /> - } - /> - )} - - {llm_config && ( - { - const llm_config = { - ...(flowSpec.config.llm_config || { temperature: 0.1 }), - config_list, - }; - onControlChange(llm_config, "llm_config"); - }} - /> - } - /> - )} - - {llm_config && llm_config.config_list.length > 0 && ( - { - const llm_config = { - ...flowSpec.config.llm_config, - temperature: value, - }; - onControlChange(llm_config, "llm_config"); - }} - /> - } - /> - )} - - { - { - const updatedFlowSpec = { - ...localFlowSpec, - skills, - }; - setLocalFlowSpec(updatedFlowSpec); - setFlowSpec(updatedFlowSpec); - }} - /> - } - /> - } - - - ); -}; - -interface SkillSelectorProps { - skills: ISkill[]; - setSkills: (skills: ISkill[]) => void; - className?: string; -} - -export const SkillSelector: React.FC = ({ - skills, - setSkills, - className, -}) => { - const [isModalVisible, setIsModalVisible] = useState(false); - const [showSkillModal, setShowSkillModal] = React.useState(false); - const [newSkill, setNewSkill] = useState(null); - - const [localSkills, setLocalSkills] = useState(skills); - const [selectedSkill, setSelectedSkill] = useState(null); - - const handleRemoveSkill = (index: number) => { - const updatedSkills = localSkills.filter((_, i) => i !== index); - setLocalSkills(updatedSkills); - setSkills(updatedSkills); - }; - - const handleAddSkill = () => { - if (newSkill) { - const updatedSkills = [...localSkills, newSkill]; - setLocalSkills(updatedSkills); - setSkills(updatedSkills); - setNewSkill(null); - } - }; - - useEffect(() => { - if (selectedSkill) { - setShowSkillModal(true); - } - }, [selectedSkill]); - - return ( - <> - { - setShowSkillModal(false); - setSelectedSkill(null); - }} - onCancel={() => { - setShowSkillModal(false); - setSelectedSkill(null); - }} - > - {selectedSkill && ( -
    -
    {selectedSkill.file_name}
    - -
    - )} -
    - -
    - {localSkills.map((skill, index) => ( -
    - { - setSelectedSkill(skill); - }} - className=" inline-block " - > - {skill.title} - - handleRemoveSkill(index)} - className="ml-1 text-primary hover:text-accent duration-300 w-4 h-4 inline-block" - /> -
    - ))} - -
    { - setIsModalVisible(true); - }} - > - add -
    -
    - - setIsModalVisible(false)} - footer={[ - , - , - ]} - > - - - - ); -}; - -export const SkillLoader = ({ - skill, - setSkill, -}: { - skill: ISkill | null; - setSkill: (skill: ISkill | null) => void; -}) => { - const [skills, setSkills] = useState([]); - const [loading, setLoading] = useState(false); - const [error, setError] = React.useState({ - status: true, - message: "All good", - }); - const serverUrl = getServerUrl(); - const { user } = React.useContext(appContext); - const listSkillsUrl = `${serverUrl}/skills?user_id=${user?.email}`; - - const fetchSkills = () => { - setError(null); - setLoading(true); - // const fetch; - const payLoad = { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }; - - const onSuccess = (data: any) => { - if (data && data.status) { - message.success(data.message); - setSkills(data.data); - if (data.data.length > 0) { - setSkill(data.data[0]); - } - } else { - message.error(data.message); - } - setLoading(false); - }; - const onError = (err: any) => { - setError(err); - message.error(err.message); - setLoading(false); - }; - fetchJSON(listSkillsUrl, payLoad, onSuccess, onError); - }; - - useEffect(() => { - fetchSkills(); - }, []); - - const skillOptions = skills.map((skill: ISkill, index: number) => ({ - label: skill.title, - value: index, - })); - return ( -
    - - - {skills && ( - <> - ({ - label: spec.config.name, - value: index, - }))} - /> -
    - )} - {/* {JSON.stringify(localAgent)} */} - - ); -}; - -export const AgentSelector = ({ - flowSpec, - setFlowSpec, -}: { - flowSpec: IAgentFlowSpec | null; - setFlowSpec: (agent: IAgentFlowSpec | null) => void; -}) => { - const [isModalVisible, setIsModalVisible] = useState(false); - - return ( -
    -
    setIsModalVisible(true)} - className="hover:bg-secondary h-full duration-300 border border-dashed rounded p-2" - > - {flowSpec && ( -
    - {flowSpec.type === "groupchat" ? ( - - ) : ( - - )} - {flowSpec.config.name} -
    - {" "} - {flowSpec.config.description || flowSpec.config.name} -
    -
    - {" "} - - {(flowSpec.skills && flowSpec.skills?.length) || 0} skills - - - | max replies: {flowSpec.config.max_consecutive_auto_reply} - -
    -
    - )} -
    - { - <> - { - setFlowSpec(agent); - }} - /> - - } -
    - ); -}; -export const FlowConfigViewer = ({ - flowConfig, - setFlowConfig, -}: { - flowConfig: IFlowConfig; - setFlowConfig: (newFlowConfig: IFlowConfig) => void; -}) => { - // Local state for sender and receiver FlowSpecs - const [senderFlowSpec, setSenderFlowSpec] = - React.useState(flowConfig.sender); - - const [localFlowConfig, setLocalFlowConfig] = - React.useState(flowConfig); - - const [receiverFlowSpec, setReceiverFlowSpec] = - React.useState(flowConfig.receiver); - - // Update the local state and propagate changes to the parent component - const updateSenderFlowSpec = (newFlowSpec: IAgentFlowSpec | null) => { - setSenderFlowSpec(newFlowSpec); - if (newFlowSpec) { - setFlowConfig({ ...flowConfig, sender: newFlowSpec }); - } - }; - - const updateReceiverFlowSpec = (newFlowSpec: IAgentFlowSpec | null) => { - setReceiverFlowSpec(newFlowSpec); - if (newFlowSpec) { - setFlowConfig({ ...flowConfig, receiver: newFlowSpec }); - } - }; - - const updateFlowConfig = (key: string, value: string) => { - // When an updatedFlowConfig is created using localFlowConfig, if the contents of FlowConfigViewer Modal are changed after the Agent Specification Modal is updated, the updated contents of the Agent Specification Modal are not saved. Fixed to localFlowConfig->flowConfig. Fixed a bug. - const updatedFlowConfig = { ...flowConfig, [key]: value }; - console.log("updatedFlowConfig: ", updatedFlowConfig); - setLocalFlowConfig(updatedFlowConfig); - setFlowConfig(updatedFlowConfig); - }; - - return ( - <> - {/*
    {flowConfig.name}
    */} - updateFlowConfig("name", e.target.value)} - /> - } - /> - - updateFlowConfig("description", e.target.value)} - /> - } - /> - - updateFlowConfig("summary_method", value)} - options={ - [ - { label: "last", value: "last" }, - { label: "none", value: "none" }, - { label: "llm", value: "llm" }, - ] as any - } - /> - } - /> -
    -
    -
    Sender
    - -
    -
    -
    Receiver
    - -
    -
    - - ); -}; - export const MonacoEditor = ({ value, editorRef, diff --git a/samples/apps/autogen-studio/frontend/src/components/header.tsx b/samples/apps/autogen-studio/frontend/src/components/header.tsx index 8ec85326923..d0adf2e0a3a 100644 --- a/samples/apps/autogen-studio/frontend/src/components/header.tsx +++ b/samples/apps/autogen-studio/frontend/src/components/header.tsx @@ -25,7 +25,7 @@ const Header = ({ meta, link }: any) => { const links: any[] = [ { name: "Build", href: "/build" }, { name: "Playground", href: "/" }, - { name: "Gallery", href: "/gallery" }, + // { name: "Gallery", href: "/gallery" }, // { name: "Data Explorer", href: "/explorer" }, ]; diff --git a/samples/apps/autogen-studio/frontend/src/components/types.ts b/samples/apps/autogen-studio/frontend/src/components/types.ts index 522682a4884..03cca34c553 100644 --- a/samples/apps/autogen-studio/frontend/src/components/types.ts +++ b/samples/apps/autogen-studio/frontend/src/components/types.ts @@ -2,14 +2,13 @@ export type NotificationType = "success" | "info" | "warning" | "error"; export interface IMessage { user_id: string; - root_msg_id: string; - msg_id?: string; role: string; content: string; - timestamp?: string; - personalize?: boolean; - ra?: string; - session_id?: string; + created_at?: string; + updated_at?: string; + session_id?: number; + connection_id?: string; + workflow_id?: number; } export interface IStatus { @@ -21,7 +20,7 @@ export interface IStatus { export interface IChatMessage { text: string; sender: "user" | "bot"; - metadata?: any; + meta?: any; msg_id: string; } @@ -30,6 +29,7 @@ export interface ILLMConfig { timeout?: number; cache_seed?: number | null; temperature: number; + max_tokens: number; } export interface IAgentConfig { @@ -42,45 +42,34 @@ export interface IAgentConfig { default_auto_reply?: string | null; code_execution_config?: boolean | string | { [key: string]: any } | null; description?: string; -} -export interface IAgentFlowSpec { - type: "assistant" | "userproxy" | "groupchat"; - config: IAgentConfig; - timestamp?: string; - id?: string; - skills?: Array; - user_id?: string; + admin_name?: string; + messages?: Array; + max_round?: number; + speaker_selection_method?: string; + allow_repeat_speaker?: boolean; } -export interface IGroupChatConfig { - agents: Array; - admin_name: string; - messages: Array; - max_round: number; - speaker_selection_method: "auto" | "round_robin" | "random"; - allow_repeat_speaker: boolean | Array; -} - -export interface IGroupChatFlowSpec { - type: "groupchat"; +export interface IAgent { + type?: "assistant" | "userproxy" | "groupchat"; config: IAgentConfig; - groupchat_config: IGroupChatConfig; - id?: string; - timestamp?: string; + created_at?: string; + updated_at?: string; + id?: number; + skills?: Array; user_id?: string; - description?: string; } -export interface IFlowConfig { +export interface IWorkflow { name: string; description: string; - sender: IAgentFlowSpec; - receiver: IAgentFlowSpec | IGroupChatFlowSpec; + sender: IAgent; + receiver: IAgent; type: "twoagents" | "groupchat"; - timestamp?: string; + created_at?: string; + updated_at?: string; summary_method?: "none" | "last" | "llm"; - id?: string; + id?: number; user_id?: string; } @@ -91,9 +80,10 @@ export interface IModelConfig { base_url?: string; api_type?: string; user_id?: string; - timestamp?: string; + created_at?: string; + updated_at?: string; description?: string; - id?: string; + id?: number; } export interface IMetadataFile { @@ -105,27 +95,29 @@ export interface IMetadataFile { } export interface IChatSession { - id: string; + id?: number; user_id: string; - timestamp: string; - flow_config: IFlowConfig; + workflow_id?: number; + created_at?: string; + updated_at?: string; name: string; } export interface IGalleryItem { - id: string; + id: number; messages: Array; session: IChatSession; tags: Array; - timestamp: string; + created_at: string; + updated_at: string; } export interface ISkill { - title: string; - file_name?: string; + name: string; content: string; - id?: string; - timestamp?: string; + id?: number; description?: string; user_id?: string; + created_at?: string; + updated_at?: string; } diff --git a/samples/apps/autogen-studio/frontend/src/components/utils.ts b/samples/apps/autogen-studio/frontend/src/components/utils.ts index 73b9f42207c..447afb6afc6 100644 --- a/samples/apps/autogen-studio/frontend/src/components/utils.ts +++ b/samples/apps/autogen-studio/frontend/src/components/utils.ts @@ -1,12 +1,11 @@ import { + IAgent, IAgentConfig, - IAgentFlowSpec, - IFlowConfig, - IGroupChatFlowSpec, ILLMConfig, IModelConfig, ISkill, IStatus, + IWorkflow, } from "./types"; export const getServerUrl = () => { @@ -66,7 +65,8 @@ export function fetchJSON( url: string | URL, payload: any = {}, onSuccess: (data: any) => void, - onError: (error: IStatus) => void + onError: (error: IStatus) => void, + onFinal: () => void = () => {} ) { return fetch(url, payload) .then(function (response) { @@ -95,6 +95,9 @@ export function fetchJSON( status: false, message: `There was an error connecting to server. (${err}) `, }); + }) + .finally(() => { + onFinal(); }); } export const capitalize = (s: string) => { @@ -243,45 +246,89 @@ export const formatDuration = (seconds: number) => { return parts.length > 0 ? parts.join(" ") : "0 sec"; }; -export const sampleAgentConfig = (user_id: string = "guestuser@gmail.com") => { - const sampleAgent: IAgentFlowSpec = { +export const sampleAgentConfig = (agent_type: string = "assistant") => { + const llm_config: ILLMConfig = { + config_list: [], + temperature: 0.1, + timeout: 600, + cache_seed: null, + max_tokens: 1000, + }; + + const userProxyConfig: IAgentConfig = { + name: "userproxy", + human_input_mode: "NEVER", + description: "User Proxy", + max_consecutive_auto_reply: 5, + system_message: "You are a helpful assistant.", + default_auto_reply: "TERMINATE", + llm_config: false, + code_execution_config: { + work_dir: null, + use_docker: false, + }, + }; + const userProxyFlowSpec: IAgent = { + type: "userproxy", + config: userProxyConfig, + }; + + const assistantConfig: IAgentConfig = { + name: "primary_assistant", + description: "Primary Assistant", + llm_config: llm_config, + human_input_mode: "NEVER", + max_consecutive_auto_reply: 8, + code_execution_config: false, + system_message: + "You are a helpful AI assistant. Solve tasks using your coding and language skills. In the following cases, suggest python code (in a python coding block) or shell script (in a sh coding block) for the user to execute. 1. When you need to collect info, use the code to output the info you need, for example, browse or search the web, download/read a file, print the content of a webpage or a file, get the current date/time, check the operating system. After sufficient info is printed and the task is ready to be solved based on your language skill, you can solve the task by yourself. 2. When you need to perform some task with code, use the code to perform the task and output the result. Finish the task smartly. Solve the task step by step if you need to. If a plan is not provided, explain your plan first. Be clear which step uses code, and which step uses your language skill. When using code, you must indicate the script type in the code block. The user cannot provide any other feedback or perform any other action beyond executing the code you suggest. The user can't modify your code. So do not suggest incomplete code which requires users to modify. Don't use a code block if it's not intended to be executed by the user. If you want the user to save the code in a file before executing it, put # filename: inside the code block as the first line. Don't include multiple code blocks in one response. Do not ask users to copy and paste the result. Instead, use 'print' function for the output when relevant. Check the execution result returned by the user. If the result indicates there is an error, fix the error and output the code again. Suggest the full code instead of partial code or code changes. If the error can't be fixed or if the task is not solved even after the code is executed successfully, analyze the problem, revisit your assumption, collect additional info you need, and think of a different approach to try. When you find an answer, verify the answer carefully. Include verifiable evidence in your response if possible. Reply 'TERMINATE' in the end when everything is done.", + }; + + const assistantFlowSpec: IAgent = { type: "assistant", - user_id: user_id, - config: { - name: "sample_assistant", - description: "Sample assistant", - llm_config: { - config_list: [ - { - model: "gpt-4-1106-preview", - }, - ], - temperature: 0.1, - timeout: 600, - cache_seed: null, - }, - human_input_mode: "NEVER", - code_execution_config: false, - max_consecutive_auto_reply: 8, - system_message: - "You are a helpful AI assistant. Solve tasks using your coding and language skills. In the following cases, suggest python code (in a python coding block) or shell script (in a sh coding block) for the user to execute. 1. When you need to collect info, use the code to output the info you need, for example, browse or search the web, download/read a file, print the content of a webpage or a file, get the current date/time, check the operating system. After sufficient info is printed and the task is ready to be solved based on your language skill, you can solve the task by yourself. 2. When you need to perform some task with code, use the code to perform the task and output the result. Finish the task smartly. Solve the task step by step if you need to. If a plan is not provided, explain your plan first. Be clear which step uses code, and which step uses your language skill. When using code, you must indicate the script type in the code block. The user cannot provide any other feedback or perform any other action beyond executing the code you suggest. The user can't modify your code. So do not suggest incomplete code which requires users to modify. Don't use a code block if it's not intended to be executed by the user. If you want the user to save the code in a file before executing it, put # filename: inside the code block as the first line. Don't include multiple code blocks in one response. Do not ask users to copy and paste the result. Instead, use 'print' function for the output when relevant. Check the execution result returned by the user. If the result indicates there is an error, fix the error and output the code again. Suggest the full code instead of partial code or code changes. If the error can't be fixed or if the task is not solved even after the code is executed successfully, analyze the problem, revisit your assumption, collect additional info you need, and think of a different approach to try. When you find an answer, verify the answer carefully. Include verifiable evidence in your response if possible. Reply 'TERMINATE' in the end when everything is done.", + config: assistantConfig, + }; + + const groupChatAssistantConfig = Object.assign( + { + admin_name: "groupchat_assistant", + messages: [], + max_round: 10, + speaker_selection_method: "auto", + allow_repeat_speaker: false, }, + assistantConfig + ); + groupChatAssistantConfig.name = "groupchat_assistant"; + groupChatAssistantConfig.system_message = + "You are a helpful assistant skilled at cordinating a group of other assistants to solve a task. "; + groupChatAssistantConfig.description = "Group Chat Assistant"; + + const groupChatFlowSpec: IAgent = { + type: "groupchat", + config: groupChatAssistantConfig, }; - return sampleAgent; + + if (agent_type === "userproxy") { + return userProxyFlowSpec; + } else if (agent_type === "assistant") { + return assistantFlowSpec; + } else if (agent_type === "groupchat") { + return groupChatFlowSpec; + } else { + return assistantFlowSpec; + } }; export const sampleWorkflowConfig = (type = "twoagents") => { - const llm_model_config: IModelConfig[] = [ - { - model: "gpt-4-1106-preview", - }, - ]; + const llm_model_config: IModelConfig[] = []; const llm_config: ILLMConfig = { config_list: llm_model_config, temperature: 0.1, timeout: 600, cache_seed: null, + max_tokens: 1000, }; const userProxyConfig: IAgentConfig = { @@ -296,7 +343,7 @@ export const sampleWorkflowConfig = (type = "twoagents") => { use_docker: false, }, }; - const userProxyFlowSpec: IAgentFlowSpec = { + const userProxyFlowSpec: IAgent = { type: "userproxy", config: userProxyConfig, }; @@ -311,12 +358,12 @@ export const sampleWorkflowConfig = (type = "twoagents") => { "You are a helpful AI assistant. Solve tasks using your coding and language skills. In the following cases, suggest python code (in a python coding block) or shell script (in a sh coding block) for the user to execute. 1. When you need to collect info, use the code to output the info you need, for example, browse or search the web, download/read a file, print the content of a webpage or a file, get the current date/time, check the operating system. After sufficient info is printed and the task is ready to be solved based on your language skill, you can solve the task by yourself. 2. When you need to perform some task with code, use the code to perform the task and output the result. Finish the task smartly. Solve the task step by step if you need to. If a plan is not provided, explain your plan first. Be clear which step uses code, and which step uses your language skill. When using code, you must indicate the script type in the code block. The user cannot provide any other feedback or perform any other action beyond executing the code you suggest. The user can't modify your code. So do not suggest incomplete code which requires users to modify. Don't use a code block if it's not intended to be executed by the user. If you want the user to save the code in a file before executing it, put # filename: inside the code block as the first line. Don't include multiple code blocks in one response. Do not ask users to copy and paste the result. Instead, use 'print' function for the output when relevant. Check the execution result returned by the user. If the result indicates there is an error, fix the error and output the code again. Suggest the full code instead of partial code or code changes. If the error can't be fixed or if the task is not solved even after the code is executed successfully, analyze the problem, revisit your assumption, collect additional info you need, and think of a different approach to try. When you find an answer, verify the answer carefully. Include verifiable evidence in your response if possible. Reply 'TERMINATE' in the end when everything is done.", }; - const assistantFlowSpec: IAgentFlowSpec = { + const assistantFlowSpec: IAgent = { type: "assistant", config: assistantConfig, }; - const workFlowConfig: IFlowConfig = { + const workFlowConfig: IWorkflow = { name: "Default Agent Workflow", description: "Default Agent Workflow", sender: userProxyFlowSpec, @@ -324,26 +371,27 @@ export const sampleWorkflowConfig = (type = "twoagents") => { type: "twoagents", }; - const groupChatAssistantConfig = Object.assign({}, assistantConfig); - groupChatAssistantConfig.name = "groupchat_assistant"; - groupChatAssistantConfig.system_message = - "You are a helpful assistant skilled at cordinating a group of other assistants to solve a task. "; - - const groupChatFlowSpec: IGroupChatFlowSpec = { - type: "groupchat", - config: groupChatAssistantConfig, - groupchat_config: { - agents: [assistantFlowSpec, assistantFlowSpec], + const groupChatAssistantConfig = Object.assign( + { admin_name: "groupchat_assistant", messages: [], max_round: 10, speaker_selection_method: "auto", allow_repeat_speaker: false, + description: "Group Chat Assistant", }, - description: "Default Group Workflow", + assistantConfig + ); + groupChatAssistantConfig.name = "groupchat_assistant"; + groupChatAssistantConfig.system_message = + "You are a helpful assistant skilled at cordinating a group of other assistants to solve a task. "; + + const groupChatFlowSpec: IAgent = { + type: "groupchat", + config: groupChatAssistantConfig, }; - const groupChatWorkFlowConfig: IFlowConfig = { + const groupChatWorkFlowConfig: IWorkflow = { name: "Default Group Workflow", description: "Default Group Workflow", sender: userProxyFlowSpec, @@ -359,79 +407,72 @@ export const sampleWorkflowConfig = (type = "twoagents") => { return workFlowConfig; }; -export const getModels = () => { - const models = [ - { - model: "gpt-4-1106-preview", - }, - { - model: "gpt-3.5-turbo-16k", - }, - { - model: "TheBloke/zephyr-7B-alpha-AWQ", - base_url: "http://localhost:8000/v1", - }, - ]; - return models; -}; - export const getSampleSkill = () => { const content = ` - ## This is a sample skill. Replace with your own skill function - ## In general, a good skill must have 3 sections: - ## 1. Imports (import libraries needed for your skill) - ## 2. Function definition AND docstrings (this helps the LLM understand what the function does and how to use it) - ## 3. Function body (the actual code that implements the function) - - import numpy as np - import matplotlib.pyplot as plt - from matplotlib import font_manager as fm - - def save_cat_ascii_art_to_png(filename='ascii_cat.png'): - """ - Creates ASCII art of a cat and saves it to a PNG file. - - :param filename: str, the name of the PNG file to save the ASCII art. - """ - # ASCII art string - cat_art = [ - " /\_/\ ", - " ( o.o ) ", - " > ^ < " - ] - - # Determine shape of output array - height = len(cat_art) - width = max(len(line) for line in cat_art) - - # Create a figure and axis to display ASCII art - fig, ax = plt.subplots(figsize=(width, height)) - ax.axis('off') # Hide axes - - # Get a monospace font - prop = fm.FontProperties(family='monospace') - - # Display ASCII art using text - for y, line in enumerate(cat_art): - ax.text(0, height-y-1, line, fontproperties=prop, fontsize=12) - - # Adjust layout - plt.tight_layout() - - # Save figure to file - plt.savefig(filename, dpi=120, bbox_inches='tight', pad_inches=0.1) - plt.close(fig)`; +from typing import List +import uuid +import requests # to perform HTTP requests +from pathlib import Path + +from openai import OpenAI + + +def generate_and_save_images(query: str, image_size: str = "1024x1024") -> List[str]: + """ + Function to paint, draw or illustrate images based on the users query or request. Generates images from a given query using OpenAI's DALL-E model and saves them to disk. Use the code below anytime there is a request to create an image. + + :param query: A natural language description of the image to be generated. + :param image_size: The size of the image to be generated. (default is "1024x1024") + :return: A list of filenames for the saved images. + """ + + client = OpenAI() # Initialize the OpenAI client + response = client.images.generate(model="dall-e-3", prompt=query, n=1, size=image_size) # Generate images + + # List to store the file names of saved images + saved_files = [] + + # Check if the response is successful + if response.data: + for image_data in response.data: + # Generate a random UUID as the file name + file_name = str(uuid.uuid4()) + ".png" # Assuming the image is a PNG + file_path = Path(file_name) + + img_url = image_data.url + img_response = requests.get(img_url) + if img_response.status_code == 200: + # Write the binary content to a file + with open(file_path, "wb") as img_file: + img_file.write(img_response.content) + print(f"Image saved to {file_path}") + saved_files.append(str(file_path)) + else: + print(f"Failed to download the image from {img_url}") + else: + print("No image data found in the response!") + + # Return the list of saved files + return saved_files + + +# Example usage of the function: +# generate_and_save_images("A cute baby sea otter") + `; const skill: ISkill = { - title: "save_cat_ascii_art_to_png", - description: "save cat ascii art to png", + name: "generate_images", + description: "Generate and save images based on a user's query.", content: content, }; return skill; }; -export const timeAgo = (dateString: string): string => { +export const timeAgo = ( + dateString: string, + returnFormatted: boolean = false +): string => { // if dateStr is empty, return empty string if (!dateString) { return ""; @@ -454,10 +495,20 @@ export const timeAgo = (dateString: string): string => { const minutesAgo = Math.floor(timeDifference / (1000 * 60)); const hoursAgo = Math.floor(minutesAgo / 60); - // Format the date into a readable format e.g. "November 27" - const options: Intl.DateTimeFormatOptions = { month: "long", day: "numeric" }; + // Format the date into a readable format e.g. "November 27, 2021, 3:45 PM" + const options: Intl.DateTimeFormatOptions = { + month: "long", + day: "numeric", + year: "numeric", + hour: "numeric", + minute: "numeric", + }; const formattedDate = timestamp.toLocaleDateString(undefined, options); + if (returnFormatted) { + return formattedDate; + } + // Determine the time difference string let timeAgoStr: string; if (minutesAgo < 1) { @@ -527,7 +578,7 @@ export const fetchVersion = () => { */ export const sanitizeConfig = ( data: any, - keys: string[] = ["api_key", "id"] + keys: string[] = ["api_key", "id", "created_at", "updated_at"] ): any => { if (Array.isArray(data)) { return data.map((item) => sanitizeConfig(item, keys)); diff --git a/samples/apps/autogen-studio/frontend/src/components/views/builder/agents.tsx b/samples/apps/autogen-studio/frontend/src/components/views/builder/agents.tsx index be8a30f7247..92215b97d9b 100644 --- a/samples/apps/autogen-studio/frontend/src/components/views/builder/agents.tsx +++ b/samples/apps/autogen-studio/frontend/src/components/views/builder/agents.tsx @@ -8,24 +8,17 @@ import { } from "@heroicons/react/24/outline"; import { Dropdown, MenuProps, Modal, message } from "antd"; import * as React from "react"; -import { IAgentFlowSpec, IStatus } from "../../types"; +import { IAgent, IStatus } from "../../types"; import { appContext } from "../../../hooks/provider"; import { fetchJSON, getServerUrl, - sampleAgentConfig, sanitizeConfig, timeAgo, truncateText, } from "../../utils"; -import { - AgentFlowSpecView, - BounceLoader, - Card, - CardHoverBar, - LaunchButton, - LoadingOverlay, -} from "../../atoms"; +import { BounceLoader, Card, CardHoverBar, LoadingOverlay } from "../../atoms"; +import { AgentFlowSpecView } from "./utils/agentconfig"; const AgentsView = ({}: any) => { const [loading, setLoading] = React.useState(false); @@ -37,25 +30,30 @@ const AgentsView = ({}: any) => { const { user } = React.useContext(appContext); const serverUrl = getServerUrl(); const listAgentsUrl = `${serverUrl}/agents?user_id=${user?.email}`; - const saveAgentsUrl = `${serverUrl}/agents`; - const deleteAgentUrl = `${serverUrl}/agents/delete`; - const [agents, setAgents] = React.useState([]); - const [selectedAgent, setSelectedAgent] = - React.useState(null); + const [agents, setAgents] = React.useState([]); + const [selectedAgent, setSelectedAgent] = React.useState(null); const [showNewAgentModal, setShowNewAgentModal] = React.useState(false); const [showAgentModal, setShowAgentModal] = React.useState(false); - const sampleAgent = sampleAgentConfig(user?.email || ""); - const [newAgent, setNewAgent] = React.useState( - sampleAgent - ); + const sampleAgent = { + config: { + name: "sample_agent", + description: "Sample agent description", + human_input_mode: "NEVER", + max_consecutive_auto_reply: 3, + system_message: "", + }, + }; + const [newAgent, setNewAgent] = React.useState(sampleAgent); - const deleteAgent = (agent: IAgentFlowSpec) => { + const deleteAgent = (agent: IAgent) => { setError(null); setLoading(true); + + const deleteAgentUrl = `${serverUrl}/agents/delete?user_id=${user?.email}&agent_id=${agent.id}`; // const fetch; const payLoad = { method: "DELETE", @@ -71,8 +69,7 @@ const AgentsView = ({}: any) => { const onSuccess = (data: any) => { if (data && data.status) { message.success(data.message); - console.log("agents", data.data); - setAgents(data.data); + fetchAgents(); } else { message.error(data.message); } @@ -98,8 +95,6 @@ const AgentsView = ({}: any) => { const onSuccess = (data: any) => { if (data && data.status) { - // message.success(data.message); - setAgents(data.data); } else { message.error(data.message); @@ -114,42 +109,6 @@ const AgentsView = ({}: any) => { fetchJSON(listAgentsUrl, payLoad, onSuccess, onError); }; - const saveAgent = (agent: IAgentFlowSpec) => { - setError(null); - setLoading(true); - // const fetch; - - const payLoad = { - method: "POST", - headers: { - Accept: "application/json", - "Content-Type": "application/json", - }, - body: JSON.stringify({ - user_id: user?.email, - agent: agent, - }), - }; - - const onSuccess = (data: any) => { - if (data && data.status) { - message.success(data.message); - // console.log("agents", data.data); - setAgents(data.data); - } else { - message.error(data.message); - } - setLoading(false); - setNewAgent(sampleAgent); - }; - const onError = (err: any) => { - setError(err); - message.error(err.message); - setLoading(false); - }; - fetchJSON(saveAgentsUrl, payLoad, onSuccess, onError); - }; - React.useEffect(() => { if (user) { // console.log("fetching messages", messages); @@ -157,7 +116,7 @@ const AgentsView = ({}: any) => { } }, []); - const agentRows = (agents || []).map((agent: IAgentFlowSpec, i: number) => { + const agentRows = (agents || []).map((agent: IAgent, i: number) => { const cardItems = [ { title: "Download", @@ -185,11 +144,10 @@ const AgentsView = ({}: any) => { let newAgent = { ...agent }; newAgent.config.name = `${agent.config.name}_copy`; newAgent.user_id = user?.email; - newAgent.timestamp = new Date().toISOString(); + newAgent.updated_at = new Date().toISOString(); if (newAgent.id) { delete newAgent.id; } - setNewAgent(newAgent); setShowNewAgentModal(true); }, @@ -206,27 +164,39 @@ const AgentsView = ({}: any) => { }, ]; return ( -
    -
    - {truncateText(agent.config.name, 25)}
    - } - onClick={() => { - setSelectedAgent(agent); - setShowAgentModal(true); - }} +
  1. + {truncateText(agent.config.name, 25)}
  2. + } + onClick={() => { + setSelectedAgent(agent); + setShowAgentModal(true); + }} + > + - + {" "} + {truncateText(agent.config.description || "", 70)} + +
    + {timeAgo(agent.updated_at || "")} +
    + + + ); }); @@ -237,26 +207,17 @@ const AgentsView = ({}: any) => { setShowAgentModal, handler, }: { - agent: IAgentFlowSpec | null; - setAgent: (agent: IAgentFlowSpec | null) => void; + agent: IAgent | null; + setAgent: (agent: IAgent | null) => void; showAgentModal: boolean; setShowAgentModal: (show: boolean) => void; - handler?: (agent: IAgentFlowSpec | null) => void; + handler?: (agent: IAgent | null) => void; }) => { - const [localAgent, setLocalAgent] = React.useState( - agent - ); + const [localAgent, setLocalAgent] = React.useState(agent); return ( - Agent Specification{" "} - - {agent?.config?.name || ""} - {" "} - - } + title={<>Agent Configuration} width={800} open={showAgentModal} onOk={() => { @@ -269,13 +230,16 @@ const AgentsView = ({}: any) => { onCancel={() => { setAgent(null); setShowAgentModal(false); + if (handler) { + handler(localAgent); + } }} + footer={[]} > {agent && ( )} {/* {JSON.stringify(localAgent)} */} @@ -344,10 +308,8 @@ const AgentsView = ({}: any) => { setAgent={setSelectedAgent} setShowAgentModal={setShowAgentModal} showAgentModal={showAgentModal} - handler={(agent: IAgentFlowSpec | null) => { - if (agent) { - saveAgent(agent); - } + handler={(agent: IAgent | null) => { + fetchAgents(); }} /> @@ -356,10 +318,8 @@ const AgentsView = ({}: any) => { setAgent={setNewAgent} setShowAgentModal={setShowNewAgentModal} showAgentModal={showNewAgentModal} - handler={(agent: IAgentFlowSpec | null) => { - if (agent) { - saveAgent(agent); - } + handler={(agent: IAgent | null) => { + fetchAgents(); }} /> @@ -397,7 +357,7 @@ const AgentsView = ({}: any) => { {agents && agents.length > 0 && (
    -
    {agentRows}
    +
      {agentRows}
    )} diff --git a/samples/apps/autogen-studio/frontend/src/components/views/builder/models.tsx b/samples/apps/autogen-studio/frontend/src/components/views/builder/models.tsx index be2c11099e3..b3bb9729a2b 100644 --- a/samples/apps/autogen-studio/frontend/src/components/views/builder/models.tsx +++ b/samples/apps/autogen-studio/frontend/src/components/views/builder/models.tsx @@ -2,7 +2,6 @@ import { ArrowDownTrayIcon, ArrowUpTrayIcon, DocumentDuplicateIcon, - ExclamationTriangleIcon, InformationCircleIcon, PlusIcon, TrashIcon, @@ -18,7 +17,13 @@ import { timeAgo, truncateText, } from "../../utils"; -import { BounceLoader, Card, CardHoverBar, LoadingOverlay } from "../../atoms"; +import { + BounceLoader, + Card, + CardHoverBar, + ControlRowView, + LoadingOverlay, +} from "../../atoms"; import TextArea from "antd/es/input/TextArea"; const ModelsView = ({}: any) => { @@ -31,8 +36,7 @@ const ModelsView = ({}: any) => { const { user } = React.useContext(appContext); const serverUrl = getServerUrl(); const listModelsUrl = `${serverUrl}/models?user_id=${user?.email}`; - const saveModelsUrl = `${serverUrl}/models`; - const deleteModelUrl = `${serverUrl}/models/delete`; + const createModelUrl = `${serverUrl}/models`; const testModelUrl = `${serverUrl}/models/test`; const defaultModel: IModelConfig = { @@ -50,28 +54,29 @@ const ModelsView = ({}: any) => { ); const [showNewModelModal, setShowNewModelModal] = React.useState(false); - const [showModelModal, setShowModelModal] = React.useState(false); + const sampleModel: IModelConfig = { + model: "gpt-4-1106-preview", + description: "Sample OpenAI GPT-4 model", + user_id: user?.email, + }; + const deleteModel = (model: IModelConfig) => { setError(null); setLoading(true); - // const fetch; + const deleteModelUrl = `${serverUrl}/models/delete?user_id=${user?.email}&model_id=${model.id}`; const payLoad = { method: "DELETE", headers: { "Content-Type": "application/json", }, - body: JSON.stringify({ - user_id: user?.email, - model: model, - }), }; const onSuccess = (data: any) => { if (data && data.status) { message.success(data.message); - setModels(data.data); + fetchModels(); } else { message.error(data.message); } @@ -111,9 +116,10 @@ const ModelsView = ({}: any) => { fetchJSON(listModelsUrl, payLoad, onSuccess, onError); }; - const saveModel = (model: IModelConfig) => { + const createModel = (model: IModelConfig) => { setError(null); setLoading(true); + model.user_id = user?.email; const payLoad = { method: "POST", @@ -121,17 +127,14 @@ const ModelsView = ({}: any) => { Accept: "application/json", "Content-Type": "application/json", }, - body: JSON.stringify({ - user_id: user?.email, - model: model, - }), + body: JSON.stringify(model), }; const onSuccess = (data: any) => { if (data && data.status) { message.success(data.message); - // console.log("models", data.data); - setModels(data.data); + const updatedModels = [data.data].concat(models || []); + setModels(updatedModels); } else { message.error(data.message); } @@ -142,7 +145,7 @@ const ModelsView = ({}: any) => { message.error(err.message); setLoading(false); }; - fetchJSON(saveModelsUrl, payLoad, onSuccess, onError); + fetchJSON(createModelUrl, payLoad, onSuccess, onError); }; React.useEffect(() => { @@ -180,7 +183,7 @@ const ModelsView = ({}: any) => { let newModel = { ...model }; newModel.model = `${model.model} Copy`; newModel.user_id = user?.email; - newModel.timestamp = new Date().toISOString(); + newModel.updated_at = new Date().toISOString(); if (newModel.id) { delete newModel.id; } @@ -200,27 +203,35 @@ const ModelsView = ({}: any) => { }, ]; return ( -
    -
    - {truncateText(model.model || "", 20)}
    - } - onClick={() => { - setSelectedModel(model); - setShowModelModal(true); - }} +
  3. + {truncateText(model.model || "", 20)}
  4. + } + onClick={() => { + setSelectedModel(model); + setShowModelModal(true); + }} + > +
    + {" "} + {truncateText(model.description || model.model || "", 70)} +
    +
    -
    - {" "} - {truncateText(model.description || model.model || "", 70)} -
    -
    {timeAgo(model.timestamp || "")}
    - - -
    - + {timeAgo(model.updated_at || "")} + + + + ); }); @@ -246,15 +257,13 @@ const ModelsView = ({}: any) => { const testModel = (model: IModelConfig) => { setModelStatus(null); setLoadingModelTest(true); + model.user_id = user?.email; const payLoad = { method: "POST", headers: { "Content-Type": "application/json", }, - body: JSON.stringify({ - user_id: user?.email, - model: model, - }), + body: JSON.stringify(model), }; const onSuccess = (data: any) => { @@ -338,62 +347,132 @@ const ModelsView = ({}: any) => { >
    Enter parameters for your model.
    - { - setLocalModel({ ...localModel, model: e.target.value }); - }} - /> - { - if (localModel) { - setLocalModel({ ...localModel, api_key: e.target.value }); - } - }} - /> - { - if (localModel) { - setLocalModel({ ...localModel, base_url: e.target.value }); - } - }} - /> - { - if (localModel) { - setLocalModel({ ...localModel, api_type: e.target.value }); - } - }} - /> - { - if (localModel) { - setLocalModel({ ...localModel, api_version: e.target.value }); - } - }} - /> -