1- import { useState , useEffect , useContext } from 'react' ;
1+ import { useContext , useEffect , useState } from 'react' ;
22import { Button } from '@nextui-org/react' ;
33import {
44 GoBook ,
@@ -33,7 +33,14 @@ import {
3333} from 'react-icons/pi' ;
3434import { EditContext } from '@/contexts/edit' ;
3535import CustomTool from '@/components/edit/configure/customTool' ;
36- import { SiAmazoneks , SiGooglecloud , SiJson , SiSupabase } from 'react-icons/si' ;
36+ import {
37+ SiAmazoneks ,
38+ SiGooglecloud ,
39+ SiJson ,
40+ SiMongodb ,
41+ SiNotion ,
42+ SiSupabase ,
43+ } from 'react-icons/si' ;
3744import { VscAzure } from 'react-icons/vsc' ;
3845import {
3946 BsClock ,
@@ -47,6 +54,8 @@ import {
4754import { MdDeleteForever } from 'react-icons/md' ;
4855import PropTypes from 'prop-types' ;
4956
57+ import { load } from '@/actions/gptscript' ;
58+
5059interface ImportsProps {
5160 tools : string [ ] | undefined ;
5261 setTools : ( tools : string [ ] ) => void ;
@@ -62,143 +71,70 @@ const Imports: React.FC<ImportsProps> = ({
6271 collapsed,
6372 enableLocal = 'true' ,
6473} ) => {
65- const [ remoteTools , setRemoteTools ] = useState < string [ ] > ( [ ] ) ;
74+ // remoteTools contains a mapping of tool references to display names for
75+ const [ remoteTools , setRemoteTools ] = useState < Map < string , string > > (
76+ new Map ( )
77+ ) ;
6678 const [ localTools , setLocalTools ] = useState < string [ ] > ( [ ] ) ;
6779 const { createNewTool, deleteLocalTool } = useContext ( EditContext ) ;
6880
81+ const updateRemoteTools = async ( remoteRefs : string [ ] ) => {
82+ const updatedRemoteTools = new Map ( remoteTools ) ;
83+ for ( const ref of remoteRefs ) {
84+ if ( updatedRemoteTools . has ( ref ) ) {
85+ // We've already the display name of this tool
86+ continue ;
87+ }
88+ updatedRemoteTools . set ( ref , await getDisplayName ( ref ) ) ;
89+ }
90+
91+ setRemoteTools ( ( ) => {
92+ const newRemoteTools = new Map ( ) ;
93+ updatedRemoteTools . forEach ( ( value , key ) => {
94+ newRemoteTools . set ( key , value ) ;
95+ } ) ;
96+ return newRemoteTools ;
97+ } ) ;
98+ } ;
99+
69100 useEffect ( ( ) => {
70101 if ( tools ) {
71- setLocalTools (
72- tools . filter (
73- ( t ) =>
74- ! (
75- t . startsWith ( 'https://' ) ||
76- t . startsWith ( 'http://' ) ||
77- t . startsWith ( 'sys.' ) || // not local, but considered remote for the purposes of this component
78- t . startsWith ( 'github.com' )
79- )
80- )
81- ) ;
82- setRemoteTools (
83- tools . filter (
84- ( t ) =>
85- t . startsWith ( 'https://' ) ||
86- t . startsWith ( 'http://' ) ||
87- t . startsWith ( 'sys.' ) || // not local, but considered remote for the purposes of this component
88- t . startsWith ( 'github.com' )
89- )
90- ) ;
102+ setLocalTools ( tools . filter ( ( t ) => ! isRemote ( t ) ) ) ;
103+ updateRemoteTools ( tools . filter ( isRemote ) ) . catch ( ( e ) => {
104+ console . error ( 'failed to update remote tools' , e ) ;
105+ } ) ;
91106 }
92107 } , [ tools ] ) ;
93108
94- // note - I know this is a bit of a mess, but it's a quick way to get icons for tools
95- const iconForTool = ( tool : string ) => {
96- switch ( tool . split ( '/' ) . pop ( ) ?. replace ( / - / g, ' ' ) ) {
97- case 'gpt4 v vision' :
98- return < BsEye className = "text-md" /> ;
99- case 'dalle image generation' :
100- return < FaPaintBrush className = "text-md" /> ;
101- case 'answers from the internet' :
102- return < GoGlobe className = "text-md" /> ;
103- case 'search website' :
104- return < GoSearch className = "text-md" /> ;
105- case 'browser' :
106- return < GoBrowser className = "text-md" /> ;
107- case 'write' :
108- return < AiOutlineSlack className = "text-md" /> ;
109- case 'trello' :
110- return < FaTrello className = "text-md" /> ;
111- case 'manage' :
112- return < PiMicrosoftOutlookLogoDuotone className = "text-md" /> ;
113- case 'knowledge' :
114- return < GoBook className = "text-md" /> ;
115- case 'structured data querier' :
116- return < PiMicrosoftExcelLogo className = "text-md" /> ;
117- case 'json query' :
118- return < SiJson className = "text-md" /> ;
119- case 'filesystem' :
120- return < BsFiles className = "text-md" /> ;
121- case 'workspace' :
122- return < GoFileDirectory className = "text-md" /> ;
123- case 'github' :
124- return < FaGithub className = "text-md" /> ;
125- case 'aws' :
126- return < FaAws className = "text-md" /> ;
127- case 'azure' :
128- return < VscAzure className = "text-md" /> ;
129- case 'digitalocean' :
130- return < FaDigitalOcean className = "text-md" /> ;
131- case 'eksctl' :
132- return < SiAmazoneks className = "text-md" /> ;
133- case 'gcp' :
134- return < SiGooglecloud className = "text-md" /> ;
135- case 'k8s' :
136- return < AiOutlineKubernetes className = "text-md" /> ;
137- case 'read-write' :
138- return < SiSupabase className = "text-md" /> ;
139- case 'supabase' :
140- return < SiSupabase className = "text-md" /> ;
141- case 'sys.append' :
142- return < AiFillFileAdd className = "text-md" /> ;
143- case 'sys.download' :
144- return < BsDownload className = "text-md" /> ;
145- case 'sys.exec' :
146- return < GoTerminal className = "text-md" /> ;
147- case 'sys.find' :
148- return < BsFiles className = "text-md" /> ;
149- case 'sys.getenv' :
150- return < BsCode className = "text-md" /> ;
151- case 'sys.http.html2text' :
152- return < FaCode className = "text-md" /> ;
153- case 'sys.http.get' :
154- return < GoGlobe className = "text-md" /> ;
155- case 'sys.http.post' :
156- return < GoGlobe className = "text-md" /> ;
157- case 'sys.ls' :
158- return < BsFolder className = "text-md" /> ;
159- case 'sys.prompt' :
160- return < GoQuestion className = "text-md" /> ;
161- case 'sys.read' :
162- return < FaGlasses className = "text-md" /> ;
163- case 'sys.remove' :
164- return < MdDeleteForever className = "text-md" /> ;
165- case 'sys.stat' :
166- return < BsSearch className = "text-md" /> ;
167- case 'sys.time.now' :
168- return < BsClock className = "text-md" /> ;
169- case 'sys.write' :
170- return < GoPencil className = "text-md" /> ;
171- }
172- } ;
109+ const deleteRemoteTool = ( tool : string ) => {
110+ // Remove the remote tool's display name mapping
111+ setRemoteTools ( ( prevRemoteTools ) => {
112+ const newRemoteTools = new Map ( prevRemoteTools ) ;
113+ newRemoteTools . delete ( tool ) ;
114+ return newRemoteTools ;
115+ } ) ;
173116
174- const handleDeleteTool = ( tool : string ) => {
117+ // Remove the tool from the assistant
175118 setTools ( tools ! . filter ( ( t ) => t !== tool ) ) ;
176119 } ;
177120
178121 return (
179122 < div className = { `${ className } ` } >
180- { remoteTools && remoteTools . length > 0 && (
123+ { remoteTools && remoteTools . size > 0 && (
181124 < div className = "grid grid-cols-1 gap-2 w-full mb-2" >
182- { remoteTools . map ( ( tool , i ) => (
125+ { Array . from ( remoteTools . keys ( ) ) . map ( ( ref , i ) => (
183126 < div key = { i } className = "flex space-x-2" >
184127 < div className = "truncate w-full border-2 dark:border-zinc-700 text-sm pl-2 rounded-lg flex justify-between items-center" >
185128 < div className = "flex items-center space-x-2" >
186- { iconForTool ( tool ) }
187- < p className = "capitalize" >
188- { tool
189- . split ( '/' )
190- . pop ( )
191- ?. replace ( / - / g, ' ' )
192- . replace ( 'sys.' , '' )
193- . replace ( '.' , ' ' ) }
194- </ p >
129+ { iconForTool ( ref ) }
130+ < p className = "capitalize" > { remoteTools . get ( ref ) ! } </ p >
195131 </ div >
196132 < Button
197133 variant = "light"
198134 isIconOnly
199135 size = "sm"
200136 startContent = { < GoTrash /> }
201- onPress = { ( ) => handleDeleteTool ( tool ) }
137+ onPress = { ( ) => deleteRemoteTool ( ref ) }
202138 />
203139 </ div >
204140 </ div >
@@ -234,10 +170,16 @@ const Imports: React.FC<ImportsProps> = ({
234170 >
235171 < ToolCatalogModal
236172 tools = { tools }
237- addTool = { ( tool ) => setTools ( [ ...( tools || [ ] ) , tool ] ) }
238- removeTool = { ( tool ) =>
239- setTools ( tools ?. filter ( ( t ) => t !== tool ) || [ ] )
240- }
173+ addTool = { ( tool ) => {
174+ setTools ( [ ...( tools || [ ] ) , tool ] ) ;
175+ } }
176+ removeTool = { ( tool ) => {
177+ if ( isRemote ( tool ) ) {
178+ deleteRemoteTool ( tool ) ;
179+ } else {
180+ setTools ( tools ?. filter ( ( t ) => t !== tool ) || [ ] ) ;
181+ }
182+ } }
241183 />
242184 { enableLocal && (
243185 < Button
@@ -266,3 +208,113 @@ Imports.propTypes = {
266208} ;
267209
268210export default Imports ;
211+
212+ function isRemote ( ref : string ) : boolean {
213+ return (
214+ ref . startsWith ( 'https://' ) ||
215+ ref . startsWith ( 'http://' ) ||
216+ ref . startsWith ( 'sys.' ) || // not local, but considered remote for the purposes of this component
217+ ref . startsWith ( 'github.com' )
218+ ) ;
219+ }
220+
221+ async function getDisplayName ( ref : string ) : Promise < string > {
222+ let displayName : string =
223+ ref . split ( '/' ) . pop ( ) ?. replace ( 'sys.' , '' ) . replace ( '.' , ' ' ) ?? ref ;
224+
225+ if ( ! ref . startsWith ( 'sys.' ) ) {
226+ const loadedTool = await load ( ref ) ;
227+ // TODO: Use entryToolId field once node-gptscript is bumped to a release containing 317e6457f056718bd9fdade18d6fbf9e0311cd46
228+ const entryToolId = ( loadedTool as any ) [ 'entryToolId' ] ;
229+ const loadedName = loadedTool . toolSet [ entryToolId ] . name ;
230+ if ( loadedName ) {
231+ displayName = loadedName ;
232+ }
233+ }
234+
235+ return displayName . replace ( / - / g, ' ' ) ;
236+ }
237+
238+ // note - I know this is a bit of a mess, but it's a quick way to get icons for tools
239+ const iconForTool = ( toolName : string | undefined ) => {
240+ switch ( toolName ) {
241+ case 'github.com/gptscript-ai/gpt4-v-vision@gateway' :
242+ return < BsEye className = "text-md" /> ;
243+ case 'github.com/gptscript-ai/dalle-image-generation@gateway' :
244+ return < FaPaintBrush className = "text-md" /> ;
245+ case 'github.com/gptscript-ai/answers-from-the-internet' :
246+ return < GoGlobe className = "text-md" /> ;
247+ case 'github.com/gptscript-ai/search-website' :
248+ return < GoSearch className = "text-md" /> ;
249+ case 'github.com/gptscript-ai/browser' :
250+ return < GoBrowser className = "text-md" /> ;
251+ case 'github.com/gptscript-ai/tools/apis/slack/write' :
252+ return < AiOutlineSlack className = "text-md" /> ;
253+ case 'github.com/gptscript-ai/tools/apis/notion/write' :
254+ return < SiNotion className = "text-md" /> ;
255+ case 'github.com/gptscript-ai/tools/apis/trello' :
256+ return < FaTrello className = "text-md" /> ;
257+ case 'github.com/gptscript-ai/tools/apis/outlook/mail/manage' :
258+ return < PiMicrosoftOutlookLogoDuotone className = "text-md" /> ;
259+ case 'github.com/gptscript-ai/tools/apis/outlook/calendar/manage' :
260+ return < PiMicrosoftOutlookLogoDuotone className = "text-md" /> ;
261+ case 'github.com/gptscript-ai/[email protected] ' :
262+ return < GoBook className = "text-md" /> ;
263+ case 'github.com/gptscript-ai/structured-data-querier' :
264+ return < PiMicrosoftExcelLogo className = "text-md" /> ;
265+ case 'github.com/gptscript-ai/json-query' :
266+ return < SiJson className = "text-md" /> ;
267+ case 'github.com/gptscript-ai/context/filesystem' :
268+ return < BsFiles className = "text-md" /> ;
269+ case 'github.com/gptscript-ai/context/workspace' :
270+ return < GoFileDirectory className = "text-md" /> ;
271+ case 'github.com/gptscript-ai/tools/clis/github' :
272+ return < FaGithub className = "text-md" /> ;
273+ case 'github.com/gptscript-ai/tools/clis/aws' :
274+ return < FaAws className = "text-md" /> ;
275+ case 'github.com/gptscript-ai/tools/clis/azure' :
276+ return < VscAzure className = "text-md" /> ;
277+ case 'github.com/gptscript-ai/tools/clis/digitalocean' :
278+ return < FaDigitalOcean className = "text-md" /> ;
279+ case 'github.com/gptscript-ai/tools/clis/eksctl' :
280+ return < SiAmazoneks className = "text-md" /> ;
281+ case 'github.com/gptscript-ai/tools/clis/atlas' :
282+ return < SiMongodb className = "text-md" /> ;
283+ case 'github.com/gptscript-ai/tools/clis/gcp' :
284+ return < SiGooglecloud className = "text-md" /> ;
285+ case 'github.com/gptscript-ai/tools/clis/k8s' :
286+ return < AiOutlineKubernetes className = "text-md" /> ;
287+ case 'github.com/gptscript-ai/tools/clis/supabase' :
288+ return < SiSupabase className = "text-md" /> ;
289+ case 'sys.append' :
290+ return < AiFillFileAdd className = "text-md" /> ;
291+ case 'sys.download' :
292+ return < BsDownload className = "text-md" /> ;
293+ case 'sys.exec' :
294+ return < GoTerminal className = "text-md" /> ;
295+ case 'sys.find' :
296+ return < BsFiles className = "text-md" /> ;
297+ case 'sys.getenv' :
298+ return < BsCode className = "text-md" /> ;
299+ case 'sys.http.html2text' :
300+ return < FaCode className = "text-md" /> ;
301+ case 'sys.http.get' :
302+ return < GoGlobe className = "text-md" /> ;
303+ case 'sys.http.post' :
304+ return < GoGlobe className = "text-md" /> ;
305+ case 'sys.ls' :
306+ return < BsFolder className = "text-md" /> ;
307+ case 'sys.prompt' :
308+ return < GoQuestion className = "text-md" /> ;
309+ case 'sys.read' :
310+ return < FaGlasses className = "text-md" /> ;
311+ case 'sys.remove' :
312+ return < MdDeleteForever className = "text-md" /> ;
313+ case 'sys.stat' :
314+ return < BsSearch className = "text-md" /> ;
315+ case 'sys.time.now' :
316+ return < BsClock className = "text-md" /> ;
317+ case 'sys.write' :
318+ return < GoPencil className = "text-md" /> ;
319+ }
320+ } ;
0 commit comments