ui: show tool number (#2071)

This commit is contained in:
Lily Delalande
2025-04-07 19:49:58 -04:00
committed by GitHub
parent 4ea14d0766
commit 490944c3f8
12 changed files with 277 additions and 67 deletions
+32
View File
@@ -13,6 +13,38 @@
"version": "1.0.17"
},
"paths": {
"/agent/tools": {
"get": {
"tags": [
"super::routes::agent"
],
"operationId": "get_tools",
"responses": {
"200": {
"description": "Tools retrieved successfully",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Tool"
}
}
}
}
},
"401": {
"description": "Unauthorized - invalid secret key"
},
"424": {
"description": "Agent not initialized"
},
"500": {
"description": "Internal server error"
}
}
}
},
"/config": {
"get": {
"tags": [
+8 -1
View File
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts
import type { Options as ClientOptions, TDataShape, Client } from '@hey-api/client-fetch';
import type { ReadAllConfigData, ReadAllConfigResponse, GetExtensionsData, GetExtensionsResponse, AddExtensionData, AddExtensionResponse, RemoveExtensionData, RemoveExtensionResponse, ProvidersData, ProvidersResponse2, ReadConfigData, RemoveConfigData, RemoveConfigResponse, UpsertConfigData, UpsertConfigResponse } from './types.gen';
import type { GetToolsData, GetToolsResponse, ReadAllConfigData, ReadAllConfigResponse, GetExtensionsData, GetExtensionsResponse, AddExtensionData, AddExtensionResponse, RemoveExtensionData, RemoveExtensionResponse, ProvidersData, ProvidersResponse2, ReadConfigData, RemoveConfigData, RemoveConfigResponse, UpsertConfigData, UpsertConfigResponse } from './types.gen';
import { client as _heyApiClient } from './client.gen';
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = ClientOptions<TData, ThrowOnError> & {
@@ -18,6 +18,13 @@ export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends
meta?: Record<string, unknown>;
};
export const getTools = <ThrowOnError extends boolean = false>(options?: Options<GetToolsData, ThrowOnError>) => {
return (options?.client ?? _heyApiClient).get<GetToolsResponse, unknown, ThrowOnError>({
url: '/agent/tools',
...options
});
};
export const readAllConfig = <ThrowOnError extends boolean = false>(options?: Options<ReadAllConfigData, ThrowOnError>) => {
return (options?.client ?? _heyApiClient).get<ReadAllConfigResponse, unknown, ThrowOnError>({
url: '/config',
+31
View File
@@ -210,6 +210,37 @@ export type UpsertConfigQuery = {
value: unknown;
};
export type GetToolsData = {
body?: never;
path?: never;
query?: never;
url: '/agent/tools';
};
export type GetToolsErrors = {
/**
* Unauthorized - invalid secret key
*/
401: unknown;
/**
* Agent not initialized
*/
424: unknown;
/**
* Internal server error
*/
500: unknown;
};
export type GetToolsResponses = {
/**
* Tools retrieved successfully
*/
200: Array<Tool>;
};
export type GetToolsResponse = GetToolsResponses[keyof GetToolsResponses];
export type ReadAllConfigData = {
body?: never;
path?: never;
+69 -63
View File
@@ -8,6 +8,7 @@ import type { View } from '../App';
import { settingsV2Enabled } from '../flags';
import { BottomMenuModeSelection } from './BottomMenuModeSelection';
import ModelsBottomBar from './settings_v2/models/bottom_bar/ModelsBottomBar';
import ToolCount from './ToolCount';
export default function BottomMenu({
hasMessages,
@@ -78,77 +79,82 @@ export default function BottomMenu({
{/* Goose Mode Selector Dropdown */}
<BottomMenuModeSelection />
{/* Model Selector Dropdown */}
{settingsV2Enabled ? (
<ModelsBottomBar dropdownRef={dropdownRef} setView={setView} />
) : (
<div className="relative flex items-center ml-auto mr-4" ref={dropdownRef}>
<div
className="flex items-center cursor-pointer"
onClick={() => setIsModelMenuOpen(!isModelMenuOpen)}
>
<span>{(currentModel?.alias ?? currentModel?.name) || 'Select Model'}</span>
{isModelMenuOpen ? (
<ChevronDown className="w-4 h-4 ml-1" />
) : (
<ChevronUp className="w-4 h-4 ml-1" />
)}
</div>
{/* Right-side section with ToolCount and Model Selector together */}
<div className="flex items-center mr-4 space-x-1">
{/* Tool count */}
<ToolCount />
{/* Model Selector Dropdown */}
{settingsV2Enabled ? (
<ModelsBottomBar dropdownRef={dropdownRef} setView={setView} />
) : (
<div className="relative flex items-center ml-0 mr-4" ref={dropdownRef}>
<div
className="flex items-center cursor-pointer"
onClick={() => setIsModelMenuOpen(!isModelMenuOpen)}
>
<span>{(currentModel?.alias ?? currentModel?.name) || 'Select Model'}</span>
{isModelMenuOpen ? (
<ChevronDown className="w-4 h-4 ml-1" />
) : (
<ChevronUp className="w-4 h-4 ml-1" />
)}
</div>
{/* Dropdown Menu */}
{isModelMenuOpen && (
<div className="absolute bottom-[24px] right-0 w-[300px] bg-bgApp rounded-lg border border-borderSubtle">
<div className="">
<ModelRadioList
className="divide-y divide-borderSubtle"
renderItem={({ model, isSelected, onSelect }) => (
<label key={model.alias ?? model.name} className="block cursor-pointer">
<div
className="flex items-center justify-between p-2 text-textStandard hover:bg-bgSubtle transition-colors"
onClick={onSelect}
>
<div>
<p className="text-sm ">{model.alias ?? model.name}</p>
<p className="text-xs text-textSubtle">
{model.subtext ?? model.provider}
</p>
</div>
<div className="relative">
<input
type="radio"
name="recentModels"
value={model.name}
checked={isSelected}
onChange={onSelect}
className="peer sr-only"
/>
<div
className="h-4 w-4 rounded-full border border-gray-400 dark:border-gray-500
{/* Dropdown Menu */}
{isModelMenuOpen && (
<div className="absolute bottom-[24px] right-0 w-[300px] bg-bgApp rounded-lg border border-borderSubtle">
<div className="">
<ModelRadioList
className="divide-y divide-borderSubtle"
renderItem={({ model, isSelected, onSelect }) => (
<label key={model.alias ?? model.name} className="block cursor-pointer">
<div
className="flex items-center justify-between p-2 text-textStandard hover:bg-bgSubtle transition-colors"
onClick={onSelect}
>
<div>
<p className="text-sm ">{model.alias ?? model.name}</p>
<p className="text-xs text-textSubtle">
{model.subtext ?? model.provider}
</p>
</div>
<div className="relative">
<input
type="radio"
name="recentModels"
value={model.name}
checked={isSelected}
onChange={onSelect}
className="peer sr-only"
/>
<div
className="h-4 w-4 rounded-full border border-gray-400 dark:border-gray-500
peer-checked:border-[6px] peer-checked:border-black dark:peer-checked:border-white
peer-checked:bg-white dark:peer-checked:bg-black
transition-all duration-200 ease-in-out"
></div>
></div>
</div>
</div>
</div>
</label>
)}
/>
<div
className="flex items-center justify-between text-textStandard p-2 cursor-pointer hover:bg-bgStandard
</label>
)}
/>
<div
className="flex items-center justify-between text-textStandard p-2 cursor-pointer hover:bg-bgStandard
border-t border-borderSubtle mt-2"
onClick={() => {
setIsModelMenuOpen(false);
setView('settings');
}}
>
<span className="text-sm">Tools and Settings</span>
<Sliders className="w-5 h-5 ml-2 rotate-90" />
onClick={() => {
setIsModelMenuOpen(false);
setView('settings');
}}
>
<span className="text-sm">Tools and Settings</span>
<Sliders className="w-5 h-5 ml-2 rotate-90" />
</div>
</div>
</div>
</div>
)}
</div>
)}
)}
</div>
)}
</div>
</div>
);
}
+80
View File
@@ -0,0 +1,80 @@
import React, { useState, useEffect } from 'react';
import { getTools } from '../api';
import { ExclamationTriangleIcon } from '@radix-ui/react-icons';
import { HammerIcon } from 'lucide-react';
import { Popover, PopoverContent, PopoverTrigger } from './ui/popover';
const SUGGESTED_MAX_TOOLS = 15;
export default function ToolCount() {
const [toolCount, setToolCount] = useState(null);
const [error, setError] = useState(false);
useEffect(() => {
const fetchTools = async () => {
try {
const response = await getTools();
if (response.error) {
console.error('failed to get tool count');
setError(true);
} else {
setToolCount(response.data.length);
}
} catch (err) {
console.error('Error fetching tools:', err);
setError(true);
}
};
fetchTools();
}, []);
if (error) {
return <div></div>;
}
if (toolCount === null) {
return <div>...</div>;
}
if (toolCount < SUGGESTED_MAX_TOOLS) {
return (
<div>
<Popover>
<PopoverTrigger asChild>
<button className="flex items-center justify-center p-0 border-0 bg-transparent cursor-pointer">
<HammerIcon size={16} />
</button>
</PopoverTrigger>
<PopoverContent className="p-3 w-auto" side="top">
<div className="space-y-1">
<p className="text-sm text-black dark:text-white">Tool count: {toolCount}</p>
</div>
</PopoverContent>
</Popover>
</div>
);
} else {
return (
<div>
<Popover>
<PopoverTrigger asChild>
<button className="flex items-center justify-center p-0 border-0 bg-transparent cursor-pointer">
<ExclamationTriangleIcon color="orange" />
</button>
</PopoverTrigger>
<PopoverContent className="p-3" side="top">
<div className="space-y-2">
<h4 className="text-sm font-medium">Warning: High Tool Count</h4>
<p className="text-xs text-black dark:text-white">
Too many tools can degrade goose's performance. Consider turning a few extensions
off.
</p>
<p className="text-xs font-medium">Tool count: {toolCount}</p>
</div>
</PopoverContent>
</Popover>
</div>
);
}
}