diff --git a/web/oss/src/components/AgentChatSlice/components/Inspector/InspectSessionButton.tsx b/web/oss/src/components/AgentChatSlice/components/Inspector/InspectSessionButton.tsx index 40fa9ce81e..e9eb2996de 100644 --- a/web/oss/src/components/AgentChatSlice/components/Inspector/InspectSessionButton.tsx +++ b/web/oss/src/components/AgentChatSlice/components/Inspector/InspectSessionButton.tsx @@ -1,25 +1,27 @@ /** - * "Inspect session" trigger (build-spec §6) — opens the docked Inspector at Session scope. The + * "Inspect session" trigger (build-spec §6) — toggles the docked Inspector at Session scope. The * first-class session entry point (the old panel only reached session view via the in-panel - * toggle). Placed in the thread's session controls. + * toggle). Placed in the thread's session controls; clicking again collapses the panel. */ import {MagnifyingGlass} from "@phosphor-icons/react" import {Button, Tooltip} from "antd" -import {useSetAtom} from "jotai" +import {useAtomValue, useSetAtom} from "jotai" -import {openInspectorSessionAtom} from "./state" +import {inspectorTargetAtom, toggleInspectorSessionAtom} from "./state" export default function InspectSessionButton({sessionId}: {sessionId: string | null}) { - const openSession = useSetAtom(openInspectorSessionAtom) + const toggleSession = useSetAtom(toggleInspectorSessionAtom) + const open = useAtomValue(inspectorTargetAtom)?.sessionId === sessionId && !!sessionId return ( - + + ) + })} + + + ) +} diff --git a/web/oss/src/components/AgentChatSlice/components/Inspector/state.ts b/web/oss/src/components/AgentChatSlice/components/Inspector/state.ts index f660a7ee51..b112d8a678 100644 --- a/web/oss/src/components/AgentChatSlice/components/Inspector/state.ts +++ b/web/oss/src/components/AgentChatSlice/components/Inspector/state.ts @@ -7,7 +7,7 @@ import {atom} from "jotai" import {atomWithStorage} from "jotai/utils" -export type InspectorLens = "timeline" | "context" | "runtime" +export type InspectorLens = "timeline" | "context" | "runtime" | "response" export type TimelineFilter = "all" | "tools" | "interactions" /** The open target. `null` = collapsed. `focusedTurn` (1-based) narrows the lenses to one turn; @@ -41,6 +41,14 @@ export const openInspectorSessionAtom = atom(null, (_get, set, sessionId: string set(inspectorTargetAtom, {sessionId, focusedTurn: null}) }) +/** Toggle the "Inspect session" trigger: close when already open on this session, else open it at + * session scope (a focused turn on the same session still counts as open, so it collapses). */ +export const toggleInspectorSessionAtom = atom(null, (get, set, sessionId: string) => { + if (!sessionId) return + const open = get(inspectorTargetAtom)?.sessionId === sessionId + set(inspectorTargetAtom, open ? null : {sessionId, focusedTurn: null}) +}) + /** Open focused on a specific turn (the "Inspect turn" trigger) — scrolls/highlights it. */ export const openInspectorTurnAtom = atom( null, diff --git a/web/oss/src/components/Playground/Components/PlaygroundHeader/index.tsx b/web/oss/src/components/Playground/Components/PlaygroundHeader/index.tsx index 6de72cecf3..981430ee82 100644 --- a/web/oss/src/components/Playground/Components/PlaygroundHeader/index.tsx +++ b/web/oss/src/components/Playground/Components/PlaygroundHeader/index.tsx @@ -14,15 +14,9 @@ import { } from "@agenta/entities/workflow" import type {EvaluatorCatalogTemplate, Workflow, WorkflowTypeColor} from "@agenta/entities/workflow" import {EntityPicker} from "@agenta/entity-ui" -import {agentTemplateLayoutAtom, AGENT_TEMPLATE_LAYOUTS} from "@agenta/entity-ui/drill-in" import {type WorkflowRevisionSelectionResult} from "@agenta/entity-ui/selection" import {useEnrichedEvaluatorOnlyAdapter as useEvaluatorOnlyAdapter} from "@agenta/entity-ui/selection" -import { - playgroundController, - isAgentModeAtomFamily, - agentChannelModeAtom, - type AgentChannelMode, -} from "@agenta/playground" +import {playgroundController, isAgentModeAtomFamily} from "@agenta/playground" import {usePlaygroundLayout} from "@agenta/playground-ui/hooks" import {textColors} from "@agenta/ui" import {VersionBadge} from "@agenta/ui/components/presentational" @@ -91,12 +85,6 @@ type PlaygroundHeaderProps = BaseContainerProps /** Entity types that represent evaluator downstream nodes */ const EVALUATOR_ENTITY_TYPES = ["workflow"] -// Response channel the agent playground speaks to the backend (transport concern, not config). -const CHANNEL_OPTIONS: {value: AgentChannelMode; label: string}[] = [ - {value: "stream", label: "Stream"}, - {value: "batch", label: "Batch"}, -] - /** Resolves a user UUID to a display name via workspace members */ const MemberAuthor: React.FC<{userId: string}> = ({userId}) => { const memberAtom = useMemo(() => workspaceMemberByIdFamily(userId), [userId]) @@ -286,12 +274,6 @@ const PlaygroundHeader: React.FC = ({className, ...divPro const onboarding = useOptionalOnboardingContext() const chromeHidden = !!onboarding && !onboarding.chromeRevealed - // Agent playground settings (page-level): config-panel layout + stream/batch response channel. - // These were previously buried in a config item's kebab; they're global, so they live here. - const layout = useAtomValue(agentTemplateLayoutAtom) - const setLayout = useSetAtom(agentTemplateLayoutAtom) - const channelMode = useAtomValue(agentChannelModeAtom) - const setChannelMode = useSetAtom(agentChannelModeAtom) // SPIKE(virtuoso): live-tunable virtualization knobs (enable + overscan + row estimate). // The whole section is hidden unless the NEXT_PUBLIC_AGENT_CHAT_VIRTUALIZATION env flag is set. const virtualizationAvailable = isAgentChatVirtualizationAvailable() @@ -304,42 +286,8 @@ const PlaygroundHeader: React.FC = ({className, ...divPro const settingsMenuItems: MenuProps["items"] = useMemo( () => [ - { - key: "view", - type: "group" as const, - label: "View", - children: AGENT_TEMPLATE_LAYOUTS.map((option) => ({ - key: `view-${option.value}`, - label: option.label, - icon: - layout === option.value ? ( - - ) : ( - - ), - onClick: () => setLayout(option.value), - })), - }, - {type: "divider" as const}, - { - key: "channel", - type: "group" as const, - label: "Response", - children: CHANNEL_OPTIONS.map((option) => ({ - key: `channel-${option.value}`, - label: option.label, - icon: - channelMode === option.value ? ( - - ) : ( - - ), - onClick: () => setChannelMode(option.value), - })), - }, ...(virtualizationAvailable ? [ - {type: "divider" as const}, { key: "virtualization", type: "group" as const, @@ -396,10 +344,6 @@ const PlaygroundHeader: React.FC = ({className, ...divPro ], [ virtualizationAvailable, - layout, - setLayout, - channelMode, - setChannelMode, virtualize, setVirtualize, overscan, @@ -871,18 +815,20 @@ const PlaygroundHeader: React.FC = ({className, ...divPro {label: "Chat", value: "chat"}, ]} /> - -