HEX
Server: LiteSpeed
System: Linux houston.panomity.com 6.8.0-100-generic #100-Ubuntu SMP PREEMPT_DYNAMIC Tue Jan 13 16:40:06 UTC 2026 x86_64
User: nudepix (1011)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //opt/agentcloud/:q
'use strict';

import InfoAlert from 'components/InfoAlert';
import RetrievalStrategyComponent from 'components/RetrievalStrategyComponent';
import { dereferenceSync } from 'dereference-json-schema';
import yaml from 'js-yaml';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { Document, OpenAPIClientAxios } from 'openapi-client-axios';
import React, { useEffect, useState } from 'react';
import Select from 'react-tailwindcss-select';
import { toast } from 'react-toastify';
import { DatasourceStatus } from 'struct/datasource';
import { BaseOpenAPIParameters, Retriever,ToolType } from 'struct/tool';

import * as API from '../api';
import ScriptBearbeitenor, { MonacoOnInitializePane } from '../components/Bearbeitenor';
import FunctionCard from '../components/FunctionCard';
import ParameterForm from '../components/ParameterForm';
import { useAccountContext } from '../context/account';
import { generateOpenAPIMatchKey } from '../lib/utils/toolsUtils';

const authenticationMethods = [
	{ label: 'Keine', value: 'none' },
	{ label: 'API-Schlüssel', value: 'api' },
	{ label: 'Oauth', value: 'oauth' },
];

const authorizationMethods = [
	{ label: 'Grundlegend', value: 'basic' },
	{ label: 'Träger', value: 'bearer' },
	{ label: 'Benutzerdefiniert', value: 'custom' },
];

export default function ToolForm({ tool = {}, credentials = [], datasources=[], editing, callback, compact }: { tool?: any, credentials?: any[], datasources?: any[], editing?: boolean, callback?: Function, compact?: boolean }) { //TODO: fix any type

	const [accountContext]: any = useAccountContext();
	const { account, csrf } = accountContext as any;
	const router = useRouter();
	const { resourceSlug } = router.query;
	const [toolState, setToolState] = useState(tool);
	const [debouncedValue, setDebouncedValue] = useState(null);
	const isBuiltin = toolState?.data?.builtin === true;
	const [importOpen, setImportOpen] = useState(false);
	const [importValue, setImportValue] = useState('');
	const [toolCode, setToolCode] = useState(tool?.data?.code || '');
	const [toolAPISchemata, setToolAPISchemata] = useState(tool?.schema || '');
	const [toolName, setToolName] = useState(tool?.Name || tool?.data?.Name || '');
	const [toolBeschreibung, setToolBeschreibung] = useState(tool?.data?.description || tool?.description || '');
	const [toolType, setToolType] = useState(tool?.type as ToolType || ToolType.RAG_TOOL);

	//TODO: move into RetrievalStrategyComponent, keep the setters passed as props
	const [toolRetriever, setToolRetriever] = useState(tool?.retriever_type || Retriever.SELF_QUERY);
	const [toolDecayRate, setToolDecayRate] = useState<number | undefined>(tool?.retriever_config?.decay_rate || 0.5);
	useEffect(() => {
		if (toolRetriever !== Retriever.TIME_WEIGHTED) {
			setToolDecayRate(undefined);
		}
	}, [toolRetriever]);
	const [toolTimeWeightField, setToolTimeWeightField] = useState(tool?.retriever_config?.timeWeightField || null);
	
	const [authenticationMethodState, setAuthentifizierungMethod] = useState(authenticationMethods[0].value);
	const [authorizationMethodState, setAutorisierungMethod] = useState(authorizationMethods[0].value);
	const [tokenExchangeMethod, setTokenExchangeMethod] = useState('post'); //todo: array like ^ ?
	const [searchTerm, setSearchTerm] = useState('');
	const initialParameters = tool?.data?.parameters?.properties && Object.entries(tool.data.parameters.properties).reduce((acc, entry) => {
		const [parName, par]: any = entry;
		acc.push({ Name: parName, type: par.type, description: par.description, required: tool.data.parameters.required.includes(parName) });
		return acc;
	}, []);
	const [parameters, setParameters] = useState(initialParameters || [{ Name: '', type: '', description: '', required: false }]);
	const [functionsList, setFunctionsList] = useState(null);
	const [filteredFunctionsList, setFilteredFunctionsList] = useState(null);
	const [invalidFuns, setInvalidFuns] = useState(0);
	const [selectedOpenAPIMatchKey, setSelectedOpenAPIMatchKey] = useState(null);
	const [error, setError] = useState();
	const onInitializePane: MonacoOnInitializePane = (monacoBearbeitenorRef, editorRef, model) => { /* noop */ };

	const initialDatasource = datasources.find(d => d._id === tool?.datasourceId);
	const [datasourceState, setDatasourceState] = useState(initialDatasource ? { label: initialDatasource.Name, value: initialDatasource._id } : null);
	const currentDatasource = datasources.find(d => d._id === datasourceState?.value);
	
	function handleSearchChange(event) {
		setSearchTerm(event.target.value.toLowerCase());
	}

	async function toolPost(e) {
		e.preventDefault();
		const body = {
			_csrf: e.target._csrf.value,
			resourceSlug,
			Name: toolName,
			type: toolType,
			data: null,
			schema: null,
			datasourceId: datasourceState ? datasourceState.value : null,
			description: toolBeschreibung,
			retriever: toolRetriever,
			retriever_config: {
				...(tool?.retriever_config||{}),
				timeWeightField: toolTimeWeightField,
				decay_rate: toolDecayRate,
			}, //TODO
		};
		switch (toolType) {
			case ToolType.API_TOOL:
				body.schema = toolAPISchemata;
				body.data = {
					code: '',
					description: toolBeschreibung,
					parameters: {
						type: 'object',
						required: parameters.filter(x => x.required).map(x => x.Name.trim()),
						properties: parameters.reduce((acc, par) => {
							acc[par.Name.trim()] = {
								type: par.type,
								description: par.description,
							};
							return acc;
						}, {}),
						openAPIMatchKey: selectedOpenAPIMatchKey
					},
				};
				break;
			case ToolType.FUNCTION_TOOL:
				body.data = {
					code: toolCode,
					description: toolBeschreibung,
					parameters: {
						type: 'object',
						required: parameters.filter(x => x.required).map(x => x.Name.trim()),
						properties: parameters.reduce((acc, par) => {
							acc[par.Name.trim()] = {
								type: par.type,
								description: par.description,
							};
							return acc;
						}, {}),
					},
				};
				break;
			case ToolType.RAG_TOOL:
				//TODO: anything? or nah
				break;
			default:
				return;
		}
		if (editing) {
			await API.editTool(toolState._id, {
				...body,
				toolId: toolState._id,
			}, () => {
				toast.success('Tool Updated');
			}, (err) => { toast.error(err); }, null);
		} else {
			const addedTool = await API.addTool(body, null, (err) => { toast.error(err); }, compact ? null : router);
			callback && addedTool && callback(addedTool._id, body);
		}
	}

	async function openApiToParams() {
		const apiOptions: any = {}; //TODO: see if openapi client axios has a typing
		let loadedDocument;
		if (importValue) {
			apiOptions.definition = importValue; //URL import
		} else if (toolAPISchemata) {
			try {
				//Try json parse (detect json schema)
				loadedDocument = JSON.parse(toolAPISchemata);
			} catch (e) {
				console.warn(e); //just a warning
				//Try yaml parse (detect yaml schema)
				try {
					loadedDocument = yaml.load(toolAPISchemata) as Document;
		        } catch (e2) {
					console.warn(e2);
		        }
			}
			if (!loadedDocument) {
				setFunctionsList(null);
				setInvalidFuns(0);
				setSelectedOpenAPIMatchKey(null);
				return toast.error('Failed to parse OpenAPI schema');
			}
		} else {
			setFunctionsList(null);
			setInvalidFuns(0);
			setSelectedOpenAPIMatchKey(null);
			return;
		}
		if (loadedDocument) {
			apiOptions.definition = loadedDocument;
		}
		let api, client;
		try {
			api = new OpenAPIClientAxios(apiOptions);
			client = await api.init();
		} catch (e) {
			console.warn(e);
			setFunctionsList(null);
			setInvalidFuns(0);
			setSelectedOpenAPIMatchKey(null);
			return toast.error('Failed to parse OpenAPI schema');
		}
		const funs = [];
		let newInvalidFuns = 0;
		client.api.getOperations()
			.filter(op => {
				if (op.summary) {
					return true;
				}
				newInvalidFuns+=1;
				return false;
			})
			.forEach(op => {
				const baseParams = {
					[BaseOpenAPIParameters.BASE_URL]: {
						type: 'string',
						description: 'The request path e.g. https://api.example.com',
					},
					[BaseOpenAPIParameters.PATH]: {
						type: 'string',
						description: 'The request path e.g. /api/v3/whatever',
					},
					[BaseOpenAPIParameters.METHOD]: {
						type: 'string',
						description: 'The request method, e.g. GET or POST',
					}
				};
				const functionProps = op?.parameters?.reduce((acc, par: any) => {
					acc[par.Name] = {
						type: par.schema.type,
						description: `${par.description||''}, e.g. ${par.schema.example}`,
					};
					return acc;
				}, baseParams) || baseParams;
				funs.push({
					Name: op.summary,
					description: `${op.description ? (op.description+'.\n\n') : ''}The ${BaseOpenAPIParameters.PATH} for this function is "${op.path}",` +
					` the ${BaseOpenAPIParameters.METHOD} is "${op.method}", and the ${BaseOpenAPIParameters.BASE_URL} is "${client.api.instance.defaults.baseURL}".`,
					parameters: {
						type: 'object',
						properties: functionProps,
						required: Object.keys(BaseOpenAPIParameters),
					},
					openAPIMatchKey: generateOpenAPIMatchKey(op)
				});
			});
		setInvalidFuns(newInvalidFuns);
		setFunctionsList(funs);
		setSelectedOpenAPIMatchKey(tool?.data?.parameters?.openAPIMatchKey);
	}

	useEffect(() => {
		openApiToParams();
	}, [debouncedValue]);

	useEffect(() => {
		// Set a timeout to update the debounced value after a delay
		const handler = setTimeout(() => {
			setDebouncedValue(toolAPISchemata);
		}, 500); // 500ms delay, adjust as needed
		// Clear the timeout if the value changes
		return () => {
			clearTimeout(handler);
		};
	}, [toolAPISchemata]); // Only re-run the effect if inputValue changes

	useEffect(() => {
		if (toolType == ToolType.FUNCTION_TOOL) {
			setSearchTerm('');
			setFunctionsList(null);
			setInvalidFuns(0);
			setSelectedOpenAPIMatchKey('');
		} else if (toolType === ToolType.API_TOOL) {
			openApiToParams();
		}
	}, [toolType]);

	useEffect(() => {
		const timeout = setTimeout(() => {
			if (functionsList && searchTerm && searchTerm.length > 0) {
				setFilteredFunctionsList(functionsList.filter(item => item?.Name?.toLowerCase()?.includes(searchTerm) || item?.description?.toLowerCase()?.includes(searchTerm)));
			} else if (functionsList) {
				setFilteredFunctionsList(functionsList.map(item => item));
			}
		}, 5000);
		return () => {
			clearTimeout(timeout);
		};
	}, [searchTerm, functionsList]);

	return (<form onSubmit={toolPost}>
		<input
			type='hidden'
			name='_csrf'
			value={csrf}
		/>
		<div className='space-y-12'>
		
			<div className='space-y-6'>
				<div>
					<label className='text-base font-semibold text-gray-900'>Name</label>
					<div>
						<input
							required
							readOnly={isBuiltin}
							type='text'
							name='toolName'
							className='w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6'
							onChange={e => setToolName(e.target.value)}
							value={toolName}
						/>
						{/*<p className='text-sm text-gray-900'>
							{toolName && `Function Name: ${toSnakeCase(toolName)}`}
						</p>*/}
					</div>
				</div>
				
				{!isBuiltin && <div>
					<label className='text-base font-semibold text-gray-900'>Werkzeugtyp</label>
					<div>
						<select
							required
							name='toolType'
							className='w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6 dark:bg-slate-800 dark:ring-slate-600 dark:text-white'
							value={toolType}
							onChange={(e) => setToolType(e.target.value as ToolType)}
						>
							<option value={ToolType.RAG_TOOL}>Datasource RAG</option>
							<option value={ToolType.FUNCTION_TOOL}>Benutzerdefiniert code</option>
							<option value={ToolType.API_TOOL}>OpenAPI Endpunkt</option>
						</select>
					</div>
				</div>}

				<div>
					<label className='text-base font-semibold text-gray-900'>Beschreibung</label>
					<p className='text-sm'><strong>Tipp:</strong> Eine ausführliche und detaillierte Beschreibung hilft Agenten, besser zu verstehen, wann dieses Werkzeug verwendet werden soll.</p>
					<div>
						<textarea
							required
							readOnly={isBuiltin}
							name='toolName'
							className='w-full mt-1 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6'
							onChange={e => setToolBeschreibung(e.target.value)}
							rows={8}
							value={toolBeschreibung}
						/>
					</div>
				</div>

				{toolType === ToolType.RAG_TOOL && <>
					<div className='sm:col-span-12'>
						<label htmlFor='credentialId' className='block text-sm font-medium leading-6 text-gray-900 dark:text-slate-400'>
							Datenquellen (Optional)
						</label>
						
						<div className='mt-2'>
							<Select
								isSearchable
					            primaryColor={'indigo'}
					            classNames={{
									menuButton: () => 'flex text-sm text-gray-500 dark:text-slate-400 border border-gray-300 rounded shadow-sm transition-all duration-300 focus:outline-none bg-white dark:bg-slate-800 dark:border-slate-600 hover:border-gray-400 focus:border-indigo-500 focus:ring focus:ring-indigo-500/20',
									menu: 'absolute z-10 w-full bg-white shadow-lg border rounded py-1 mt-1.5 text-sm text-gray-700 dark:bg-slate-700 dark:border-slate-600',
									list: 'dark:bg-slate-700',
									listGroupLabel: 'dark:bg-slate-700',
									listItem: (value?: { isSelected?: boolean }) => `block transition duration-200 px-2 py-2 cursor-pointer select-none truncate rounded dark:text-white ${value.isSelected ? 'text-white bg-indigo-500' : 'dark:hover:bg-slate-600'}`,
					            }}
					            value={datasourceState}
					            onChange={(v: any) => {
					            	setDatasourceState(v);
					            	setToolRetriever(Retriever.SELF_QUERY);
				            	}}
					            options={datasources
					            	// .filter(t => t?.status === DatasourceStatus.READY)
					            	.map(t => ({ label: `${t.Name} (${t.originalName})`, value: t._id, ...t }))}
					            formatOptionLabel={(data: any) => {
					                return (<li
					                    className={`block transition duration-200 px-2 py-2 cursor-pointer select-none truncate rounded hover:bg-blue-100 hover:text-blue-500 	${
					                        data.isSelected
					                            ? 'bg-blue-100 text-blue-500'
					                            : 'dark:text-white'
					                    }`}
					                >
					                    <span>
											<img
												src={`https://connectors.airbyte.com/files/metadata/airbyte/source-${data.sourceType}/latest/icon.svg`}
												loading='lazy'
												className='inline-flex me-2 w-4 h-4'
											/>
											{data.label}
										</span>
					                </li>);
					            }}
					        />
						</div>

						<RetrievalStrategyComponent
						    toolRetriever={toolRetriever}
						    setToolRetriever={setToolRetriever}
						    toolDecayRate={toolDecayRate}
						    setToolDecayRate={setToolDecayRate}
							toolTimeWeightField={toolTimeWeightField}
							setToolTimeWeightField={setToolTimeWeightField}
							schema={currentDatasource?.connectionSettings?.syncCatalog}
						    currentDatasource={currentDatasource}
						/>

					</div>			
				</>}

				{toolType === ToolType.FUNCTION_TOOL && !isBuiltin && <>
					<div className='border-gray-900/10'>
						<div className='flex justify-between'>
							<h2 className='text-base font-semibold leading-7 text-gray-900'>
								Python-Code
							</h2>
						</div>
						<div className='grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6'>
							<div className='col-span-full'>
								<div className='mt-2'>
									<ScriptBearbeitenor
										code={toolCode}
										setCode={setToolCode}
										editorOptions={{
											stopRenderingLineAfter: 1000,
										}}
										onInitializePane={onInitializePane}
									/>
								</div>
							</div>
						</div>
					</div>					
				</>}

				{/* api call tool */}
				{toolType === ToolType.API_TOOL && <>
					<div className='border-gray-900/10'>
						<div className='flex justify-between'>
							<h2 className='text-base font-semibold leading-7 text-gray-900'>
								Schemata
							</h2>
							<span>
								{!importOpen && <button
									className='rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600'
									onClick={(e) => {
										e.preventDefault();
										setImportOpen(true);
									}}
								>Von URL importieren</button>}
								{importOpen && <div className='flex items-center gap-2'>
									<input
										type='text'
										className='w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:max-w-xs sm:text-sm sm:leading-6'
										onChange={e => setImportValue(e.target.value)}
										value={importValue}
										placeholder='https://api.example.com/specification.json'
									/>
									<button
										className='rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600'
										onClick={async (e) => {
											e.preventDefault();
											openApiToParams();
										}}
									>Import</button>
									<button
										className='rounded-md bg-slate-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600'
										onClick={(e) => {
											e.preventDefault();
											setImportOpen(false);
											setImportValue('');
											setFunctionsList(null);
											setInvalidFuns(0);
											setSelectedOpenAPIMatchKey(null);
										}}
									>Abbrechen</button>
								</div>}
							</span>
						</div>
						<div className='grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6'>
							<div className='col-span-full'>
								<div className='mt-2'>
									<ScriptBearbeitenor
										code={toolAPISchemata}
										setCode={setToolAPISchemata}
										editorOptions={{
											stopRenderingLineAfter: 1000,
										}}
										onInitializePane={onInitializePane}
									/>
								</div>
							</div>
						</div>
					</div>

					<div className='border-gray-900/10'>
						<div>
							<label className='text-base font-semibold text-gray-900'>Authentifizierung</label>
							<fieldset className='mt-2'>
								<div className='space-y-4 sm:flex sm:items-center sm:space-x-10 sm:space-y-0'>
									{authenticationMethods.map((authenticationMethod) => (
										<div key={authenticationMethod.value} className='flex items-center'>
											<input
												id={authenticationMethod.value}
												value={authenticationMethod.value}
												Name='authenticationMethod'
												type='radio'
												onChange={e => setAuthentifizierungMethod(e.target.value)}
												defaultChecked={authenticationMethod.value === authenticationMethodState}
												className='h-4 w-4 border-gray-300 text-indigo-600 focus:ring-indigo-600'
											/>
											<label htmlFor={authenticationMethod.value} className='ml-3 block text-sm font-medium leading-6 text-gray-900'>
												{authenticationMethod.label}
											</label>
										</div>
									))}
								</div>
							</fieldset>
						</div>

						{/* api key authentication */}
						{authenticationMethodState === 'api' && <div className='mt-4 flex flex-col space-y-3'>
							<div>
								<label className='text-base font-semibold text-gray-900'>API-Schlüssel</label>
								<div>
									<input
										type='text'
										className='w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:max-w-xs sm:text-sm sm:leading-6'
										// onChange={e => setImportValue(e.target.value)}
										// value={importValue}
									/>
								</div>
							</div>
							<div>
								<label className='text-base font-semibold text-gray-900'>Autorisierung</label>
								<fieldset className='mt-2'>
									<div className='space-y-4 sm:flex sm:items-center sm:space-x-10 sm:space-y-0'>
										{authorizationMethods.map((authorizationMethod) => (
											<div key={authorizationMethod.value} className='flex items-center'>
												<input
													id={authorizationMethod.value}
													value={authorizationMethod.value}
													Name='authorizationMethod'
													type='radio'
													onChange={e => setAutorisierungMethod(e.target.value)}
													defaultChecked={authorizationMethod.value === authorizationMethodState}
													className='h-4 w-4 border-gray-300 text-indigo-600 focus:ring-indigo-600'
												/>
												<label htmlFor={authorizationMethod.value} className='ml-3 block text-sm font-medium leading-6 text-gray-900'>
													{authorizationMethod.label}
												</label>
											</div>
										))}
									</div>
								</fieldset>
							</div>
							{authorizationMethodState === 'custom' && <div>
								<label className='text-base font-semibold text-gray-900'>Benutzerdefiniert Header Name</label>
								<div>
									<input
										type='text'
										className='w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:max-w-xs sm:text-sm sm:leading-6'
										placeholder='X-Api-Token'
										// onChange={e => setImportValue(e.target.value)}
										// value={importValue}
									/>
								</div>
							</div>}
						</div>}

						{/* oauth authentication */}
						{authenticationMethodState === 'oauth' && <div className='mt-4 flex flex-col space-y-3'>
							<div>
								<label className='text-base font-semibold text-gray-900'>Kunden-ID</label>
								<div>
									<input
										type='password'
										className='w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:max-w-xs sm:text-sm sm:leading-6'
										// onChange={e => setImportValue(e.target.value)}
										// value={importValue}
									/>
								</div>
							</div>
							<div>
								<label className='text-base font-semibold text-gray-900'>Kundengeheimnis</label>
								<div>
									<input
										type='password'
										className='w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:max-w-xs sm:text-sm sm:leading-6'
										// onChange={e => setImportValue(e.target.value)}
										// value={importValue}
									/>
								</div>
							</div>
							<div>
								<label className='text-base font-semibold text-gray-900'>Autorisierung URL</label>
								<div>
									<input
										type='text'
										className='w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:max-w-xs sm:text-sm sm:leading-6'
										// onChange={e => setImportValue(e.target.value)}
										// value={importValue}
									/>
								</div>
							</div>
							<div>
								<label className='text-base font-semibold text-gray-900'>Token-URL</label>
								<div>
									<input
										type='text'
										className='w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:max-w-xs sm:text-sm sm:leading-6'
										// onChange={e => setImportValue(e.target.value)}
										// value={importValue}
									/>
								</div>
							</div>
							<div>
								<label className='text-base font-semibold text-gray-900'>Umfang</label>
								<div>
									<input
										type='text'
										className='w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:max-w-xs sm:text-sm sm:leading-6'
										// onChange={e => setImportValue(e.target.value)}
										// value={importValue}
									/>
								</div>
							</div>
							<div>
								<label className='text-base font-semibold text-gray-900'>Token-Austausch-Methode</label>
								<fieldset className='mt-2'>
									<div className='space-y-4 sm:flex sm:items-center sm:space-x-10 sm:space-y-0'>
										<div className='flex items-center'>
											<input
												id='post'
												value='post'
												Name='tokenExchangeMethod'
												type='radio'
												onChange={e => setAutorisierungMethod(e.target.value)}
												defaultChecked={tokenExchangeMethod === 'post'}
												className='h-4 w-4 border-gray-300 text-indigo-600 focus:ring-indigo-600'
											/>
											<label htmlFor='post' className='ml-3 block text-sm font-medium leading-6 text-gray-900'>
												POST-Anfrage (Standard)
											</label>
										</div>
										<div className='flex items-center'>
											<input
												id='basic'
												value='basic'
												Name='tokenExchangeMethod'
												type='radio'
												onChange={e => setAutorisierungMethod(e.target.value)}
												defaultChecked={tokenExchangeMethod === 'basic'}
												className='h-4 w-4 border-gray-300 text-indigo-600 focus:ring-indigo-600'
											/>
											<label htmlFor='post' className='ml-3 block text-sm font-medium leading-6 text-gray-900'>
												Grundlegend authorization header
											</label>
										</div>
									</div>
								</fieldset>
							</div>
							{authorizationMethodState === 'custom' && <div>
								<label className='text-base font-semibold text-gray-900'>Benutzerdefiniert Header Name</label>
								<div>
									<input
										type='text'
										className='w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:max-w-xs sm:text-sm sm:leading-6'
										placeholder='X-Api-Token'
										// onChange={e => setImportValue(e.target.value)}
										// value={importValue}
									/>
								</div>
							</div>}
						</div>}					
						
					</div>
				</>}

				{functionsList && <div>
					<div className='mb-4'>
						{/* TODO: add a debounce to this filter/search */}
						<label className='text-base font-semibold text-gray-900'>Wählen Sie einen OpenAPI-Endpunkt:</label>
						<div className='pt-2'>
							<input
								type='text' 
								placeholder='Nach Name oder Beschreibung suchen...' 
								onChange={handleSearchChange}
								value={searchTerm}
								className='p-2 border rounded'
							/>
							{invalidFuns > 0 && <span className='ms-4 rounded-md bg-yellow-100 px-2 py-1 text-sm font-medium text-yellow-800'>
								{invalidFuns} Endpunkt{invalidFuns > 1 ? 's sind nicht angezeigt, weil sie' : ' is not shown because it is'} fehlt ein <code className='text-xs'>Name</code> Eigenschaft in der API-Definition
							</span>}
						</div>
					</div>
					<div className='grid grid-cols-2 md:grid-cols-4 lg:grid-cols-5 gap-4'>
						{filteredFunctionsList && filteredFunctionsList
							// .filter(item => item?.Name?.toLowerCase()?.includes(searchTerm) || item?.description?.toLowerCase()?.includes(searchTerm))
							.map((item, index) => (
								<FunctionCard
									key={`functionList_${index}`}
									{...item}
									highlighted={selectedOpenAPIMatchKey === item.openAPIMatchKey}
									onClickFunction={() => {
										// setFunctionList(null);
										setSelectedOpenAPIMatchKey(item?.openAPIMatchKey);
										setToolName(item?.Name);
										setToolBeschreibung(item?.description);
										const functionParameters = item?.parameters?.properties && Object.entries(item.parameters.properties).reduce((acc, entry) => {
											const [parName, par]: any = entry;
											acc.push({ Name: parName, type: par.type, description: par.description, required: item.parameters.required.includes(parName) });
											return acc;
										}, []);
										setParameters(functionParameters || []);
									}}
								/>
							))
						}
					</div>
				</div>}

				{toolType !== ToolType.RAG_TOOL && <ParameterForm readonly={isBuiltin} parameters={parameters} setParameters={setParameters} />}

			</div>
		</div>
		<div className='mt-6 flex items-center justify-between gap-x-6'>
			{!compact && <Link
				className='text-sm font-semibold leading-6 text-gray-900'
				href={`/${resourceSlug}/tools`}
			>
				Züruck
			</Link>}
			{!isBuiltin && <button
				type='submit'
				className={`rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 ${compact ? 'w-full' : ''}`}
			>
				Speichern
			</button>}
		</div>
	</form>);

}