ToolkitEditor.svelte 8.29 KB
Newer Older
Timothy J. Baek's avatar
Timothy J. Baek committed
1
<script>
Timothy J. Baek's avatar
Timothy J. Baek committed
2
	import { getContext, createEventDispatcher, onMount } from 'svelte';
Timothy J. Baek's avatar
Timothy J. Baek committed
3
4
5

	const i18n = getContext('i18n');

Timothy J. Baek's avatar
Timothy J. Baek committed
6
	import CodeEditor from '$lib/components/common/CodeEditor.svelte';
Timothy J. Baek's avatar
Timothy J. Baek committed
7
	import { goto } from '$app/navigation';
Timothy J. Baek's avatar
Timothy J. Baek committed
8
	import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
Timothy J. Baek's avatar
Timothy J. Baek committed
9

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
10
11
	const dispatch = createEventDispatcher();

Timothy J. Baek's avatar
Timothy J. Baek committed
12
	let formElement = null;
Timothy J. Baek's avatar
Timothy J. Baek committed
13
	let loading = false;
Timothy J. Baek's avatar
Timothy J. Baek committed
14
	let showConfirm = false;
Timothy J. Baek's avatar
Timothy J. Baek committed
15

Timothy J. Baek's avatar
Timothy J. Baek committed
16
	export let edit = false;
Timothy J. Baek's avatar
Timothy J. Baek committed
17
	export let clone = false;
Timothy J. Baek's avatar
Timothy J. Baek committed
18
19
20
21

	export let id = '';
	export let name = '';
	export let meta = {
Timothy J. Baek's avatar
Timothy J. Baek committed
22
23
		description: ''
	};
Timothy J. Baek's avatar
Timothy J. Baek committed
24
	export let content = '';
Timothy J. Baek's avatar
Timothy J. Baek committed
25

Timothy J. Baek's avatar
Timothy J. Baek committed
26
	$: if (name && !edit && !clone) {
Timothy J. Baek's avatar
Timothy J. Baek committed
27
28
29
30
		id = name.replace(/\s+/g, '_').toLowerCase();
	}

	let codeEditor;
Timothy J. Baek's avatar
Timothy J. Baek committed
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
	let boilerplate = `import os
import requests
from datetime import datetime


class Tools:
    def __init__(self):
        pass

    # Add your custom tools using pure Python code here, make sure to add type hints
    # Use Sphinx-style docstrings to document your tools, they will be used for generating tools specifications
    # Please refer to function_calling_filter_pipeline.py file from pipelines project for an example

    def get_user_name_and_email_and_id(self, __user__: dict = {}) -> str:
        """
        Get the user name, Email and ID from the user object.
        """

        # Do not include :param for __user__ in the docstring as it should not be shown in the tool's specification
        # The session user object will be passed as a parameter when the function is called

        print(__user__)
        result = ""

        if "name" in __user__:
            result += f"User: {__user__['name']}"
        if "id" in __user__:
            result += f" (ID: {__user__['id']})"
        if "email" in __user__:
            result += f" (Email: {__user__['email']})"

        if result == "":
            result = "User: Unknown"

        return result

    def get_current_time(self) -> str:
        """
        Get the current time in a more human-readable format.
        :return: The current time.
        """

        now = datetime.now()
        current_time = now.strftime("%I:%M:%S %p")  # Using 12-hour format with AM/PM
        current_date = now.strftime(
            "%A, %B %d, %Y"
        )  # Full weekday, month name, day, and year

        return f"Current Date and Time = {current_date}, {current_time}"

    def calculator(self, equation: str) -> str:
        """
        Calculate the result of an equation.
        :param equation: The equation to calculate.
        """

        # Avoid using eval in production code
        # https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
        try:
            result = eval(equation)
            return f"{equation} = {result}"
        except Exception as e:
            print(e)
            return "Invalid equation"

    def get_current_weather(self, city: str) -> str:
        """
        Get the current weather for a given city.
        :param city: The name of the city to get the weather for.
        :return: The current weather information or an error message.
        """
        api_key = os.getenv("OPENWEATHER_API_KEY")
        if not api_key:
            return (
                "API key is not set in the environment variable 'OPENWEATHER_API_KEY'."
            )

        base_url = "http://api.openweathermap.org/data/2.5/weather"
        params = {
            "q": city,
            "appid": api_key,
            "units": "metric",  # Optional: Use 'imperial' for Fahrenheit
        }

        try:
            response = requests.get(base_url, params=params)
            response.raise_for_status()  # Raise HTTPError for bad responses (4xx and 5xx)
            data = response.json()

            if data.get("cod") != 200:
                return f"Error fetching weather data: {data.get('message')}"

            weather_description = data["weather"][0]["description"]
            temperature = data["main"]["temp"]
            humidity = data["main"]["humidity"]
            wind_speed = data["wind"]["speed"]

            return f"Weather in {city}: {temperature}°C"
        except requests.RequestException as e:
            return f"Error fetching weather data: {str(e)}"
`;
Timothy J. Baek's avatar
Timothy J. Baek committed
132
133
134

	const saveHandler = async () => {
		loading = true;
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
135
136
137
138
139
140
		dispatch('save', {
			id,
			name,
			meta,
			content
		});
Timothy J. Baek's avatar
Timothy J. Baek committed
141
142
143
144
	};

	const submitHandler = async () => {
		if (codeEditor) {
Timothy J. Baek's avatar
Timothy J. Baek committed
145
			const res = await codeEditor.formatPythonCodeHandler();
Timothy J. Baek's avatar
Timothy J. Baek committed
146
147
148
149
150
151
152
153
154
155
156

			if (res) {
				console.log('Code formatted successfully');
				saveHandler();
			}
		}
	};
</script>

<div class=" flex flex-col justify-between w-full overflow-y-auto h-full">
	<div class="mx-auto w-full md:px-0 h-full">
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
157
		<form
Timothy J. Baek's avatar
Timothy J. Baek committed
158
			bind:this={formElement}
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
159
160
			class=" flex flex-col max-h-[100dvh] h-full"
			on:submit|preventDefault={() => {
161
162
163
164
165
				if (edit) {
					submitHandler();
				} else {
					showConfirm = true;
				}
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
166
167
			}}
		>
Timothy J. Baek's avatar
Timothy J. Baek committed
168
169
170
171
172
173
			<div class="mb-2.5">
				<button
					class="flex space-x-1"
					on:click={() => {
						goto('/workspace/tools');
					}}
Timothy J. Baek's avatar
Timothy J. Baek committed
174
					type="button"
Timothy J. Baek's avatar
Timothy J. Baek committed
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
				>
					<div class=" self-center">
						<svg
							xmlns="http://www.w3.org/2000/svg"
							viewBox="0 0 20 20"
							fill="currentColor"
							class="w-4 h-4"
						>
							<path
								fill-rule="evenodd"
								d="M17 10a.75.75 0 01-.75.75H5.612l4.158 3.96a.75.75 0 11-1.04 1.08l-5.5-5.25a.75.75 0 010-1.08l5.5-5.25a.75.75 0 111.04 1.08L5.612 9.25H16.25A.75.75 0 0117 10z"
								clip-rule="evenodd"
							/>
						</svg>
					</div>
					<div class=" self-center font-medium text-sm">{$i18n.t('Back')}</div>
				</button>
			</div>

			<div class="flex flex-col flex-1 overflow-auto h-0 rounded-lg">
				<div class="w-full mb-2 flex flex-col gap-1.5">
					<div class="flex gap-2 w-full">
						<input
							class="w-full px-3 py-2 text-sm font-medium bg-gray-50 dark:bg-gray-850 dark:text-gray-200 rounded-lg outline-none"
							type="text"
SimonOriginal's avatar
SimonOriginal committed
200
							placeholder={$i18n.t('Toolkit Name (e.g. My ToolKit)')}
Timothy J. Baek's avatar
Timothy J. Baek committed
201
202
203
204
205
							bind:value={name}
							required
						/>

						<input
Timothy J. Baek's avatar
Timothy J. Baek committed
206
							class="w-full px-3 py-2 text-sm font-medium disabled:text-gray-300 dark:disabled:text-gray-700 bg-gray-50 dark:bg-gray-850 dark:text-gray-200 rounded-lg outline-none"
Timothy J. Baek's avatar
Timothy J. Baek committed
207
							type="text"
SimonOriginal's avatar
SimonOriginal committed
208
							placeholder={$i18n.t('Toolkit ID (e.g. my_toolkit)')}
Timothy J. Baek's avatar
Timothy J. Baek committed
209
210
							bind:value={id}
							required
Timothy J. Baek's avatar
Timothy J. Baek committed
211
							disabled={edit}
Timothy J. Baek's avatar
Timothy J. Baek committed
212
213
214
215
216
						/>
					</div>
					<input
						class="w-full px-3 py-2 text-sm font-medium bg-gray-50 dark:bg-gray-850 dark:text-gray-200 rounded-lg outline-none"
						type="text"
SimonOriginal's avatar
SimonOriginal committed
217
						placeholder={$i18n.t('Toolkit Description (e.g. A toolkit for performing various operations)')}
Timothy J. Baek's avatar
Timothy J. Baek committed
218
219
220
221
222
223
						bind:value={meta.description}
						required
					/>
				</div>

				<div class="mb-2 flex-1 overflow-auto h-0 rounded-lg">
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
224
225
226
					<CodeEditor
						bind:value={content}
						bind:this={codeEditor}
Timothy J. Baek's avatar
Timothy J. Baek committed
227
						{boilerplate}
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
228
						on:save={() => {
Timothy J. Baek's avatar
Timothy J. Baek committed
229
230
231
							if (formElement) {
								formElement.requestSubmit();
							}
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
232
233
						}}
					/>
Timothy J. Baek's avatar
Timothy J. Baek committed
234
235
				</div>

Timothy J. Baek's avatar
Timothy J. Baek committed
236
237
238
				<div class="pb-3 flex justify-between">
					<div class="flex-1 pr-3">
						<div class="text-xs text-gray-500 line-clamp-2">
SimonOriginal's avatar
SimonOriginal committed
239
240
							<span class=" font-semibold dark:text-gray-200">{$i18n.t('Warning:')}</span>
							{$i18n.t('Tools are a function calling system with arbitrary code execution')} <br />—
Timothy J. Baek's avatar
Timothy J. Baek committed
241
							<span class=" font-medium dark:text-gray-400"
SimonOriginal's avatar
SimonOriginal committed
242
								>{$i18n.t('don't install random tools from sources you don't trust.')}</span
Timothy J. Baek's avatar
Timothy J. Baek committed
243
244
245
							>
						</div>
					</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
246

Timothy J. Baek's avatar
Timothy J. Baek committed
247
248
					<button
						class="px-3 py-1.5 text-sm font-medium bg-emerald-600 hover:bg-emerald-700 text-gray-50 transition rounded-lg"
Timothy J. Baek's avatar
Timothy J. Baek committed
249
						type="submit"
Timothy J. Baek's avatar
Timothy J. Baek committed
250
251
252
253
254
					>
						{$i18n.t('Save')}
					</button>
				</div>
			</div>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
255
		</form>
Timothy J. Baek's avatar
Timothy J. Baek committed
256
257
	</div>
</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
258
259
260
261
262
263
264
265
266

<ConfirmDialog
	bind:show={showConfirm}
	on:confirm={() => {
		submitHandler();
	}}
>
	<div class="text-sm text-gray-500">
		<div class=" bg-yellow-500/20 text-yellow-700 dark:text-yellow-200 rounded-lg px-4 py-3">
SimonOriginal's avatar
SimonOriginal committed
267
			<div>{$i18n.t('Please carefully review the following warnings:')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
268
269

			<ul class=" mt-1 list-disc pl-4 text-xs">
SimonOriginal's avatar
SimonOriginal committed
270
271
				<li>{$i18n.t('Tools have a function calling system that allows arbitrary code execution.')}</li>
				<li>{$i18n.t('Do not install tools from sources you do not fully trust.')}</li>
Timothy J. Baek's avatar
Timothy J. Baek committed
272
273
274
275
			</ul>
		</div>

		<div class="my-3">
SimonOriginal's avatar
SimonOriginal committed
276
277
278
			{$i18n.t(
				'I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.'
			)}
Timothy J. Baek's avatar
Timothy J. Baek committed
279
280
281
		</div>
	</div>
</ConfirmDialog>