├── tests ├── __init__.py ├── test_serialization.py ├── test_backend.py └── setup │ └── local_agent.py ├── src └── agdebugger │ ├── __init__.py │ ├── intervention_utils.py │ ├── scoring.py │ ├── cli.py │ ├── log.py │ ├── types.py │ ├── intervention.py │ ├── utils.py │ ├── app.py │ ├── serialization.py │ └── backend.py ├── MANIFEST.in ├── frontend ├── src │ ├── vite-env.d.ts │ ├── components │ │ ├── MessageDisplays │ │ │ ├── MessageDisplayProps.ts │ │ │ ├── EmptyMessageDisplay.tsx │ │ │ ├── DefaultMessageDisplay.tsx │ │ │ ├── DisplayMessage.tsx │ │ │ └── GroupChatDisplay.tsx │ │ ├── AgentList.tsx │ │ ├── viz │ │ │ ├── viz-utils.ts │ │ │ ├── ScoreTooltip.tsx │ │ │ ├── MessageTooltip.tsx │ │ │ └── MessageHistoryChart.tsx │ │ ├── LogList.tsx │ │ ├── LogDisplay.tsx │ │ ├── common │ │ │ └── GrowTextarea.tsx │ │ ├── MessageList.tsx │ │ ├── RunControls.tsx │ │ ├── MessageQueue.tsx │ │ ├── AgentCard.tsx │ │ ├── ConversationOverview.tsx │ │ ├── MessageCard.tsx │ │ └── SendMessage.tsx │ ├── index.css │ ├── main.tsx │ ├── api.ts │ ├── utils │ │ ├── default-messages.ts │ │ └── colours.ts │ ├── context │ │ ├── AllowActionsContext.tsx │ │ └── HoveredMessageContext.tsx │ ├── shared-types.ts │ └── App.tsx ├── postcss.config.js ├── .prettierrc ├── vite.config.ts ├── tsconfig.node.json ├── .eslintrc.cjs ├── index.html ├── tsconfig.json ├── tailwind.config.js └── package.json ├── .github └── screenshots │ └── agdebugger_sc.png ├── CODE_OF_CONDUCT.md ├── DEV.md ├── examples ├── magentic-agents │ └── scenario.py └── local-agents │ ├── scenario.py │ └── local_agent.py ├── LICENSE ├── SUPPORT.md ├── pyproject.toml ├── .gitignore ├── README.md └── SECURITY.md /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/agdebugger/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft frontend/dist -------------------------------------------------------------------------------- /frontend/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /.github/screenshots/agdebugger_sc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/agdebugger/HEAD/.github/screenshots/agdebugger_sc.png -------------------------------------------------------------------------------- /frontend/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /frontend/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@trivago/prettier-plugin-sort-imports"], 3 | "importOrder": ["^[./]"], 4 | "importOrderSeparation": true 5 | } 6 | -------------------------------------------------------------------------------- /frontend/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react-swc"; 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }); 8 | -------------------------------------------------------------------------------- /frontend/src/components/MessageDisplays/MessageDisplayProps.ts: -------------------------------------------------------------------------------- 1 | export interface MessageDisplayProps { 2 | allowEdit?: boolean; 3 | messageDict: unknown; 4 | setMessage: (message: unknown) => void; 5 | type: string; 6 | } 7 | -------------------------------------------------------------------------------- /frontend/src/components/MessageDisplays/EmptyMessageDisplay.tsx: -------------------------------------------------------------------------------- 1 | import type { MessageDisplayProps } from "./MessageDisplayProps"; 2 | 3 | const EmptyMessageDisplay: React.FC = () => { 4 | return
; 5 | }; 6 | 7 | export default EmptyMessageDisplay; 8 | -------------------------------------------------------------------------------- /frontend/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true 9 | }, 10 | "include": ["vite.config.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /frontend/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | /* 6 | https://stackoverflow.com/questions/66416614/how-to-create-scrollable-element-in-tailwind-without-a-scrollbar 7 | */ 8 | @layer utilities { 9 | /* Chrome, Safari and Opera */ 10 | .no-scrollbar::-webkit-scrollbar { 11 | display: none; 12 | } 13 | 14 | .no-scrollbar { 15 | -ms-overflow-style: none; /* IE and Edge */ 16 | scrollbar-width: none; /* Firefox */ 17 | } 18 | } -------------------------------------------------------------------------------- /frontend/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | "eslint:recommended", 6 | "plugin:@typescript-eslint/recommended", 7 | "plugin:react-hooks/recommended", 8 | ], 9 | ignorePatterns: ["dist", ".eslintrc.cjs"], 10 | parser: "@typescript-eslint/parser", 11 | plugins: ["react-refresh"], 12 | rules: { 13 | "react-refresh/only-export-components": [ 14 | "warn", 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /DEV.md: -------------------------------------------------------------------------------- 1 | # Development 2 | 3 | ## Backend 4 | 5 | ```sh 6 | # Install editable 7 | pip install -e . 8 | 9 | AGDEBUGGER_BACKEND_SERVE_UI=FALSE agdebugger scenario:get_agent_team --port 8123 10 | ``` 11 | 12 | ## Frontend 13 | 14 | First time 15 | 16 | ```sh 17 | cd frontend 18 | # Create a .env.development.local file with the required API URL 19 | echo "VITE_AGDEBUGGER_FRONTEND_API_URL=http://localhost:8123/api" > .env.development.local 20 | npm install 21 | ``` 22 | 23 | Later can just launch dev server 24 | 25 | ```sh 26 | cd frontend 27 | npm run dev 28 | ``` 29 | -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | AGDebugger 11 | 12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /frontend/src/components/AgentList.tsx: -------------------------------------------------------------------------------- 1 | import type { AgentName } from "../shared-types"; 2 | import AgentCard from "./AgentCard.tsx"; 3 | 4 | interface AgentListProps { 5 | agents: AgentName[]; 6 | } 7 | 8 | const AgentList: React.FC = (props) => { 9 | return ( 10 |
11 |
12 | {props.agents.map((agent) => ( 13 | 14 | ))} 15 |
16 |
17 |
18 | ); 19 | }; 20 | 21 | export default AgentList; 22 | -------------------------------------------------------------------------------- /frontend/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | 4 | import App from "./App.tsx"; 5 | import { AllowActionsProvider } from "./context/AllowActionsContext.tsx"; 6 | import { HoveredMessageProvider } from "./context/HoveredMessageContext.tsx"; 7 | import "./index.css"; 8 | 9 | ReactDOM.createRoot(document.getElementById("root")!).render( 10 | 11 | 12 | 13 | 14 | 15 | 16 | , 17 | ); 18 | -------------------------------------------------------------------------------- /frontend/src/api.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | let url = location.protocol + "//" + location.host + "/api"; 4 | // Check if AGDEBUGGER_FRONTEND_API_URL exists in the environment variables 5 | if (import.meta.env.VITE_AGDEBUGGER_FRONTEND_API_URL) { 6 | url = import.meta.env.VITE_AGDEBUGGER_FRONTEND_API_URL; 7 | } 8 | export const api = axios.create({ 9 | baseURL: url, 10 | }); 11 | 12 | export const step = async (effectFn: () => void) => { 13 | api 14 | .post("/step") 15 | .then((response) => { 16 | console.log("Message processed:", response.data); 17 | effectFn(); 18 | }) 19 | .catch((error) => console.error("Error processing next:", error)); 20 | }; 21 | -------------------------------------------------------------------------------- /frontend/src/utils/default-messages.ts: -------------------------------------------------------------------------------- 1 | const TEXT_MESSAGE = { source: "user", content: "", type: "TextMessage" }; 2 | 3 | export const DEFAULT_MESSAGES: { 4 | [key: string]: unknown; 5 | } = { 6 | GroupChatStart: { 7 | messages: [TEXT_MESSAGE], 8 | type: "GroupChatStart", 9 | }, 10 | GroupChatAgentResponse: { 11 | agent_response: { 12 | chat_message: TEXT_MESSAGE, 13 | }, 14 | type: "GroupChatAgentResponse", 15 | }, 16 | GroupChatMessage: { 17 | message: TEXT_MESSAGE, 18 | type: "GroupChatMessage", 19 | }, 20 | GroupChatTermination: { 21 | message: { type: "StopMessage", content: "", source: "user" }, 22 | type: "GroupChatTermination", 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /frontend/src/components/viz/viz-utils.ts: -------------------------------------------------------------------------------- 1 | import type { Message } from "../../shared-types"; 2 | 3 | export const getMessageField = ( 4 | message: Message, 5 | field: "type" | "sender" | "recipient", 6 | ): string | null => { 7 | if (field === "type") { 8 | // @ts-expect-error might have type 9 | const type = message.message.type; 10 | if (type === undefined) { 11 | return "Thought"; 12 | } 13 | return type; 14 | } 15 | return message[field]; 16 | }; 17 | 18 | // colors 19 | // schemeTableau10 without orange 20 | export const COLOR_RANGE = [ 21 | "#4e79a7", 22 | "#e15759", 23 | "#76b7b2", 24 | "#59a14f", 25 | "#edc949", 26 | "#af7aa1", 27 | "#ff9da7", 28 | "#9c755f", 29 | ]; 30 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true, 22 | "noImplicitAny": false 23 | }, 24 | "include": ["src"], 25 | "references": [{ "path": "./tsconfig.node.json" }] 26 | } 27 | -------------------------------------------------------------------------------- /examples/magentic-agents/scenario.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | from autogen_agentchat.teams import MagenticOneGroupChat 4 | from autogen_agentchat.ui import Console 5 | from autogen_ext.agents.web_surfer import MultimodalWebSurfer 6 | from autogen_ext.models.openai import OpenAIChatCompletionClient 7 | 8 | 9 | async def get_agent_team(): 10 | model_client = OpenAIChatCompletionClient(model="gpt-4o") 11 | 12 | surfer = MultimodalWebSurfer( 13 | "WebSurfer", 14 | model_client=model_client, 15 | ) 16 | team = MagenticOneGroupChat([surfer], model_client=model_client) 17 | 18 | return team 19 | 20 | 21 | async def main() -> None: 22 | team = await get_agent_team() 23 | 24 | await Console(team.run_stream(task="What is the weather in Seattle?")) 25 | 26 | 27 | if __name__ == "__main__": 28 | asyncio.run(main()) 29 | -------------------------------------------------------------------------------- /examples/local-agents/scenario.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | from autogen_agentchat.conditions import MaxMessageTermination 4 | from autogen_agentchat.teams import RoundRobinGroupChat 5 | from autogen_ext.models.openai import OpenAIChatCompletionClient 6 | from local_agent import LocalAgent 7 | 8 | 9 | async def get_agent_team(): 10 | model_client = OpenAIChatCompletionClient(model="gpt-4o") 11 | 12 | agent1 = LocalAgent("LOCAL_AGENT_1", model_client=model_client) 13 | agent2 = LocalAgent("LOCAL_AGENT_2", model_client=model_client) 14 | termination = MaxMessageTermination(10) 15 | team = RoundRobinGroupChat([agent1, agent2], termination_condition=termination) 16 | 17 | return team 18 | 19 | 20 | async def main() -> None: 21 | team = await get_agent_team() 22 | 23 | result = await team.run(task="0") 24 | print("\n\nFINAL RESULT", result) 25 | 26 | 27 | if __name__ == "__main__": 28 | asyncio.run(main()) 29 | -------------------------------------------------------------------------------- /frontend/src/components/viz/ScoreTooltip.tsx: -------------------------------------------------------------------------------- 1 | import type { ScoreResult } from "../../shared-types"; 2 | 3 | interface MessageTooltipProps { 4 | scoreResult?: ScoreResult; 5 | } 6 | 7 | const renderScoreTooltipContent = (scoreResult?: ScoreResult) => { 8 | if (scoreResult == undefined) { 9 | return "No score for this session. Try running score function"; 10 | } 11 | 12 | const header = scoreResult.passed ? "Session passing!" : "Session failing!"; 13 | 14 | return ( 15 |
16 |
{header}
17 |
Expected: {scoreResult.expected}
18 |
Actual: {scoreResult.actual}
19 |
20 | ); 21 | }; 22 | const ScoreTooltip: React.FC = ({ scoreResult }) => { 23 | return ( 24 |
25 | {renderScoreTooltipContent(scoreResult)} 26 |
27 | ); 28 | }; 29 | 30 | export default ScoreTooltip; 31 | -------------------------------------------------------------------------------- /frontend/src/components/LogList.tsx: -------------------------------------------------------------------------------- 1 | import { AngleRight, AngleDown } from "flowbite-react-icons/outline"; 2 | import { useState } from "react"; 3 | 4 | import type { LogMessage } from "../shared-types"; 5 | import LogDisplay from "./LogDisplay"; 6 | 7 | interface LogListProps { 8 | logs: LogMessage[]; 9 | } 10 | 11 | const LogList: React.FC = ({ logs }) => { 12 | const [show, setShow] = useState(false); 13 | 14 | return ( 15 |
16 | 26 | 27 | {show && logs.map((log, index) => )} 28 |
29 | ); 30 | }; 31 | 32 | export default LogList; 33 | -------------------------------------------------------------------------------- /frontend/src/components/viz/MessageTooltip.tsx: -------------------------------------------------------------------------------- 1 | import type { Message } from "../../shared-types"; 2 | 3 | interface MessageTooltipProps { 4 | message: Message; 5 | } 6 | 7 | function getRecipient(message: Message): string { 8 | // @ts-expect-error might have type 9 | if (message.message?.type == undefined) return ""; 10 | 11 | const recep = message.recipient ?? "Group"; 12 | 13 | return " → " + recep; 14 | } 15 | 16 | const MessageTooltip: React.FC = ({ message }) => { 17 | return ( 18 |
19 |
20 | Timestamp {message.timestamp} 21 |
22 |
23 | {message.sender ?? "User"} 24 | {getRecipient(message)} 25 |
26 | {/* @ts-expect-error might have type */} 27 |
{message?.message?.type}
28 |
29 | ); 30 | }; 31 | 32 | export default MessageTooltip; 33 | -------------------------------------------------------------------------------- /frontend/src/context/AllowActionsContext.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, useState, useContext, ReactNode } from "react"; 2 | 3 | interface AllowActionsContextType { 4 | allowActions: boolean; 5 | setAllowActions: (flag: boolean) => void; 6 | } 7 | 8 | const AllowActionsContext = createContext( 9 | undefined, 10 | ); 11 | 12 | interface ProviderProps { 13 | children: ReactNode; 14 | } 15 | 16 | export const AllowActionsProvider: React.FC = ({ children }) => { 17 | const [allowActions, setAllowActions] = useState(true); 18 | 19 | return ( 20 | 21 | {children} 22 | 23 | ); 24 | }; 25 | 26 | // Custom hook to use the context 27 | export const useAllowActions = (): AllowActionsContextType => { 28 | const context = useContext(AllowActionsContext); 29 | if (context === undefined) { 30 | throw new Error( 31 | "useAllowActions must be used within a AllowActionsProvider", 32 | ); 33 | } 34 | return context; 35 | }; 36 | -------------------------------------------------------------------------------- /frontend/src/components/LogDisplay.tsx: -------------------------------------------------------------------------------- 1 | import type { LogMessage } from "../shared-types"; 2 | 3 | interface LogDisplayProps { 4 | log: LogMessage; 5 | } 6 | 7 | function getLogColor(level: string): string { 8 | if (level === "INFO") { 9 | return "text-stone-500"; 10 | } 11 | if (level === "DEBUG") { 12 | return "text-indigo-500"; 13 | } 14 | if (level === "ERROR") { 15 | return "text-red-500"; 16 | } 17 | 18 | return "text-black"; 19 | } 20 | 21 | const LogDisplay: React.FC = ({ log }) => { 22 | const toggled = true; 23 | 24 | return ( 25 |
28 |
29 | 30 | {log.level}:{" "} 31 | 32 | {String(log.message).substring(0, 200)} 33 | {String(log.message).length > 200 ? "..." : ""} 34 |
35 |
36 | ); 37 | }; 38 | 39 | export default LogDisplay; 40 | -------------------------------------------------------------------------------- /src/agdebugger/intervention_utils.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | 3 | import aiofiles 4 | from autogen_core import SingleThreadedAgentRuntime 5 | 6 | from .intervention import AgDebuggerInterventionHandler 7 | 8 | #### utils for running intervention handler from python script 9 | STATE_CACHE = {} 10 | 11 | 12 | async def save_agent_state_to_cache(runtime: SingleThreadedAgentRuntime, timestep: int) -> None: 13 | checkpoint = await runtime.save_state() 14 | STATE_CACHE[timestep] = checkpoint 15 | 16 | 17 | async def write_cache_and_history(ihandler: AgDebuggerInterventionHandler) -> None: 18 | # run_id = int(time.time()) 19 | run_id = "" 20 | 21 | hist_path = f"history{run_id}.pickle" 22 | cache_path = f"cache{run_id}.pickle" 23 | 24 | await write_file_async(hist_path, ihandler.history) 25 | await write_file_async(cache_path, STATE_CACHE) 26 | 27 | print("Saved AgDebugger cache files to: ", [hist_path, cache_path]) 28 | 29 | 30 | async def write_file_async(path, data): 31 | async with aiofiles.open(path, "wb") as f: 32 | buffer = pickle.dumps(data) 33 | await f.write(buffer) 34 | -------------------------------------------------------------------------------- /frontend/tailwind.config.js: -------------------------------------------------------------------------------- 1 | import flowbite from "flowbite-react/tailwind"; 2 | 3 | /** @type {import('tailwindcss').Config} */ 4 | export default { 5 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}", flowbite.content()], 6 | theme: { 7 | extend: { 8 | colors: { 9 | // cyan from https://flowbite-svelte.com/docs/pages/colors 10 | primary: { 11 | 50: "#ecfeff", 12 | 100: "#cffafe", 13 | 200: "#a5f3fc", 14 | 300: "#67e8f9", 15 | 400: "#22d3ee", 16 | 500: "#06b6d4", 17 | 600: "#0891b2", 18 | 700: "#0e7490", 19 | 800: "#155e75", 20 | 900: "#164e63", 21 | }, 22 | // slate 23 | secondary: { 24 | 50: "#f8fafc", 25 | 100: "#f1f5f9", 26 | 200: "#e2e8f0", 27 | 300: "#cbd5e1", 28 | 400: "#94a3b8", 29 | 500: "#64748b", 30 | 600: "#475569", 31 | 700: "#334155", 32 | 800: "#1e293b", 33 | 900: "#0f172a", 34 | }, 35 | }, 36 | }, 37 | }, 38 | plugins: [flowbite.plugin()], 39 | }; 40 | -------------------------------------------------------------------------------- /frontend/src/components/MessageDisplays/DefaultMessageDisplay.tsx: -------------------------------------------------------------------------------- 1 | import GrowTextarea from "../common/GrowTextarea"; 2 | import type { MessageDisplayProps } from "./MessageDisplayProps"; 3 | 4 | const DefaultMessageDisplay: React.FC = ({ 5 | messageDict, 6 | allowEdit = false, 7 | setMessage, 8 | }) => { 9 | function getValue(message): string { 10 | return JSON.stringify(message, null, 2); 11 | } 12 | 13 | function handleEdit(newValue: string) { 14 | const newMessage = JSON.parse(newValue); 15 | setMessage(newMessage); 16 | } 17 | 18 | return ( 19 |
20 | {allowEdit ? ( 21 | { 23 | handleEdit(e.target.value); 24 | }} 25 | value={getValue(messageDict)} 26 | className="border-white bg-white font-mono hover:bg-gray-50 focus:bg-gray-50" 27 | /> 28 | ) : ( 29 |
30 |
31 |             {getValue(messageDict)}
32 |           
33 |
34 | )} 35 |
36 | ); 37 | }; 38 | 39 | export default DefaultMessageDisplay; 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /frontend/src/components/MessageDisplays/DisplayMessage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import DefaultMessageDisplay from "./DefaultMessageDisplay"; 4 | import EmptyMessageDisplay from "./EmptyMessageDisplay"; 5 | import GroupChatDisplay from "./GroupChatDisplay"; 6 | import type { MessageDisplayProps } from "./MessageDisplayProps"; 7 | 8 | const MESSAGE_DISPLAYS: { [key: string]: React.FC } = { 9 | default: DefaultMessageDisplay, 10 | None: EmptyMessageDisplay, 11 | RequestReplyMessage: EmptyMessageDisplay, 12 | ResetMessage: EmptyMessageDisplay, 13 | GroupChatRequestPublish: EmptyMessageDisplay, 14 | GroupChatReset: EmptyMessageDisplay, 15 | GroupChatStart: GroupChatDisplay, 16 | GroupChatMessage: GroupChatDisplay, 17 | GroupChatAgentResponse: GroupChatDisplay, 18 | GroupChatTermination: GroupChatDisplay, 19 | }; 20 | 21 | const DisplayMessage: React.FC = (props) => { 22 | const ComponentToRender = 23 | props.type != undefined ? MESSAGE_DISPLAYS[props.type] : undefined; 24 | 25 | if (!ComponentToRender) { 26 | return React.createElement(MESSAGE_DISPLAYS["default"], props); 27 | } 28 | 29 | return React.createElement(ComponentToRender, props); 30 | }; 31 | 32 | export default DisplayMessage; 33 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # TODO: The maintainer of this repo has not yet edited this file 2 | 3 | **REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project? 4 | 5 | - **No CSS support:** Fill out this template with information about how to file issues and get help. 6 | - **Yes CSS support:** Fill out an intake form at [aka.ms/onboardsupport](https://aka.ms/onboardsupport). CSS will work with/help you to determine next steps. 7 | - **Not sure?** Fill out an intake as though the answer were "Yes". CSS will help you decide. 8 | 9 | *Then remove this first heading from this SUPPORT.MD file before publishing your repo.* 10 | 11 | # Support 12 | 13 | ## How to file issues and get help 14 | 15 | This project uses GitHub Issues to track bugs and feature requests. Please search the existing 16 | issues before filing new issues to avoid duplicates. For new issues, file your bug or 17 | feature request as a new Issue. 18 | 19 | For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE 20 | FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER 21 | CHANNEL. WHERE WILL YOU HELP PEOPLE?**. 22 | 23 | ## Microsoft Support Policy 24 | 25 | Support for this **PROJECT or PRODUCT** is limited to the resources listed above. 26 | -------------------------------------------------------------------------------- /frontend/src/context/HoveredMessageContext.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, useState, useContext, ReactNode } from "react"; 2 | 3 | // Define the shape of the context state 4 | interface HoveredMessageContextType { 5 | hoveredMessageId: number | undefined; 6 | setHoveredMessageId: (id: number | undefined) => void; 7 | } 8 | 9 | // Create the context with a default value 10 | const HoveredMessageContext = createContext< 11 | HoveredMessageContextType | undefined 12 | >(undefined); 13 | 14 | // Create a provider component 15 | interface HoveredMessageProviderProps { 16 | children: ReactNode; 17 | } 18 | 19 | export const HoveredMessageProvider: React.FC = ({ 20 | children, 21 | }) => { 22 | const [hoveredMessageId, setHoveredMessageId] = useState( 23 | undefined, 24 | ); 25 | 26 | return ( 27 | 30 | {children} 31 | 32 | ); 33 | }; 34 | 35 | // Custom hook to use the context 36 | export const useHoveredMessage = (): HoveredMessageContextType => { 37 | const context = useContext(HoveredMessageContext); 38 | if (context === undefined) { 39 | throw new Error( 40 | "useHoveredMessage must be used within a HoveredMessageProvider", 41 | ); 42 | } 43 | return context; 44 | }; 45 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "agdebugger-frontend", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@column-resizer/react": "^1.3.0", 14 | "@tippyjs/react": "^4.2.6", 15 | "axios": "^1.7.2", 16 | "d3": "^7.9.0", 17 | "flowbite-react": "^0.9.0", 18 | "flowbite-react-icons": "^1.0.7", 19 | "lodash": "^4.17.21", 20 | "react": "^18.2.0", 21 | "react-dom": "^18.2.0", 22 | "react-force-graph": "^1.44.4" 23 | }, 24 | "devDependencies": { 25 | "@trivago/prettier-plugin-sort-imports": "^4.3.0", 26 | "@types/d3": "^7.4.3", 27 | "@types/react": "^18.2.66", 28 | "@types/react-dom": "^18.2.22", 29 | "@types/react-modal": "^3.16.3", 30 | "@typescript-eslint/eslint-plugin": "^7.2.0", 31 | "@typescript-eslint/parser": "^7.2.0", 32 | "@vitejs/plugin-react-swc": "^3.5.0", 33 | "autoprefixer": "^10.4.19", 34 | "eslint": "^8.57.0", 35 | "eslint-plugin-react-hooks": "^4.6.0", 36 | "eslint-plugin-react-refresh": "^0.4.6", 37 | "postcss": "^8.4.38", 38 | "prettier": "^3.3.2", 39 | "tailwindcss": "^3.4.3", 40 | "typescript": "^5.2.2", 41 | "vite": "^5.2.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/agdebugger/scoring.py: -------------------------------------------------------------------------------- 1 | """Similar to agbench tabulate utils for checking task completion of current session""" 2 | 3 | from typing import Callable, List 4 | 5 | from .types import ContentMessage, ScoreResult, TimeStampedMessage 6 | from .utils import parse_message_content 7 | 8 | 9 | def human_eval_scorer(messages: List[ContentMessage]) -> ScoreResult: 10 | for m in messages: 11 | if "ALL TESTS PASSED !#!#" in m.content: 12 | return ScoreResult( 13 | passed=True, first_timestamp=m.timestamp, expected=None, actual=None 14 | ) 15 | return ScoreResult(passed=False, first_timestamp=None, expected=None, actual=None) 16 | 17 | 18 | def run_score_func( 19 | messages: List[TimeStampedMessage], 20 | score_function: Callable[[List[ContentMessage]], ScoreResult] | None = None, 21 | ) -> ScoreResult | None: 22 | """ 23 | Tags each message with profile tags. 24 | 25 | Returns: array of same legnth 26 | """ 27 | if score_function is None: 28 | return None 29 | 30 | content_messages = [] 31 | 32 | for message in messages: 33 | parsed_message = parse_message_content(message.message) 34 | content_messages.append( 35 | ContentMessage(timestamp=message.timestamp, content=parsed_message.content) 36 | ) 37 | 38 | return score_function(content_messages) 39 | 40 | 41 | SCORE_FUNCS = {"human_eval": human_eval_scorer} 42 | -------------------------------------------------------------------------------- /frontend/src/utils/colours.ts: -------------------------------------------------------------------------------- 1 | const COLOURS_MANUAL = [ 2 | "#59AA1E", // green 3 | "#1E59AA", // blue 4 | "#AA1E59", // red 5 | "#fb923c", // orange 6 | "#a78bfa", // violet 7 | ]; 8 | 9 | const getColour = (str: string, colorArray: string[]): string => { 10 | if (str) { 11 | const hashCode = [...str].reduce((acc, char) => { 12 | acc = (acc << 5) - acc + char.charCodeAt(0); 13 | return acc & acc; 14 | }, 0); 15 | 16 | const index = Math.abs(hashCode) % colorArray.length; 17 | return colorArray[index]; 18 | } 19 | 20 | return "#9ca3af"; // Default gray 400 if undefined or empty 21 | }; 22 | 23 | export const stringToColour = (input: unknown): string => { 24 | if (input === undefined || input === null) { 25 | return "#9ca3af"; // Default gray 400 if undefined 26 | } 27 | 28 | const strInput = String(input); 29 | const lower = strInput.toLowerCase(); 30 | 31 | if (lower === "send") { 32 | return "#59AA1E"; 33 | } 34 | if (lower === "response") { 35 | return "#1E59AA"; 36 | } 37 | if (lower === "publish") { 38 | return "#AA1E59"; 39 | } 40 | 41 | return getColour(strInput, COLOURS_MANUAL); 42 | }; 43 | 44 | export const messageTypeFormat = ( 45 | messageName: string | undefined | null, 46 | ): string | undefined | null => { 47 | // map certain message types to a more readable names 48 | 49 | if (messageName === "PublishMessageEnvelope") { 50 | return "Publish"; 51 | } 52 | 53 | if (messageName === "SendMessageEnvelope") { 54 | return "Send"; 55 | } 56 | 57 | if (messageName === "ResponseMessageEnvelope") { 58 | return "Response"; 59 | } 60 | 61 | return messageName; 62 | }; 63 | -------------------------------------------------------------------------------- /frontend/src/components/common/GrowTextarea.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, useRef } from "react"; 2 | 3 | interface TextareaProps { 4 | value: string | undefined; 5 | onChange: React.ChangeEventHandler; 6 | placeholder?: string; 7 | className?: string; 8 | } 9 | 10 | const GrowTextarea: React.FC = ({ 11 | value, 12 | onChange, 13 | placeholder = "Enter text", 14 | className, 15 | }) => { 16 | const textareaRef = useRef(null); 17 | const [internalValue, setInternalValue] = useState(value || ""); 18 | 19 | useEffect(() => { 20 | if (value !== internalValue) { 21 | setInternalValue(value || ""); 22 | } 23 | // eslint-disable-next-line react-hooks/exhaustive-deps 24 | }, [value]); 25 | 26 | const handleChange = (e: React.ChangeEvent) => { 27 | setInternalValue(e.target.value); 28 | onChange(e); 29 | }; 30 | 31 | useEffect(() => { 32 | if (textareaRef.current) { 33 | textareaRef.current.style.height = "auto"; 34 | textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`; 35 | } 36 | }, [internalValue]); 37 | 38 | return ( 39 |