File size: 7,086 Bytes
39eb06d
1778c9e
15094ac
3b86586
f415c95
1778c9e
f415c95
3b86586
1778c9e
52c6f5c
05e5873
52c6f5c
 
5ac02d2
1778c9e
5ac02d2
 
0ffe6c3
1778c9e
5ac02d2
f415c95
 
 
 
 
1778c9e
f415c95
1778c9e
 
 
 
 
 
7e80e42
f415c95
1778c9e
 
 
 
 
 
 
 
 
 
 
 
52c6f5c
05e5873
39eb06d
 
f459835
d5e14b5
7e29a25
f415c95
 
 
d5e14b5
 
427a3a2
f415c95
 
 
1778c9e
f415c95
 
 
 
 
 
1778c9e
f415c95
 
1778c9e
 
f415c95
1778c9e
f415c95
 
 
1778c9e
d5e14b5
f415c95
 
7e29a25
0ffe6c3
7e29a25
1778c9e
f415c95
d5e14b5
f415c95
3d94876
d5e14b5
 
1778c9e
 
 
 
 
 
 
 
 
 
 
 
d7cd63b
3b86586
d7cd63b
 
 
 
 
 
 
 
 
 
 
 
 
05e5873
d7cd63b
 
 
 
 
 
05e5873
52c6f5c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
05e5873
 
 
 
 
 
 
 
 
 
 
 
7c08d14
 
 
 
 
 
 
 
 
 
 
 
39eb06d
1778c9e
05e5873
 
52c6f5c
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
<script lang="ts">
	import type { ConversationClass } from "$lib/state/conversations.svelte.js";
	import { maxAllowedTokens } from "$lib/utils/business.svelte.js";
	import { cn } from "$lib/utils/cn.js";
	import { isNumber } from "$lib/utils/is.js";
	import { watch } from "runed";
	import IconX from "~icons/carbon/close";
	import ExtraParamsModal, { openExtraParamsModal } from "./extra-params-modal.svelte";
	import { GENERATION_CONFIG_KEYS, GENERATION_CONFIG_SETTINGS } from "./generation-config-settings.js";
	import MCPModal from "./mcp-modal.svelte";
	import StructuredOutputModal, { openStructuredOutputModal } from "./structured-output-modal.svelte";
	import { mcpServers } from "$lib/state/mcps.svelte.js";
	import { isMcpEnabled } from "$lib/constants.js";
	interface Props {
		conversation: ConversationClass;
		classNames?: string;
	}

	const { conversation, classNames = "" }: Props = $props();

	const maxTokens = $derived(maxAllowedTokens(conversation));

	watch(
		() => maxTokens,
		() => {
			const curr = conversation.data.config.max_tokens;
			if (!curr || curr <= maxTokens) return;
			conversation.update({
				config: {
					...conversation.data.config,
					max_tokens: maxTokens,
				},
			});
		},
	);

	type Config = (typeof conversation)["data"]["config"];
	function updateConfigKey<K extends keyof Config>(k: K, v: Config[K]) {
		conversation.update({
			...conversation.data,
			config: {
				...conversation.data.config,
				[k]: v,
			},
		});
	}

	let editingMCP = $state(false);
	const extraParamsLen = $derived(Object.keys(conversation.data.extraParams ?? {}).length);
</script>

<div class="flex flex-col gap-y-7 {classNames}">
	{#each GENERATION_CONFIG_KEYS as key}
		{@const { label, min, step } = GENERATION_CONFIG_SETTINGS[key]}
		{@const isMaxTokens = key === "max_tokens"}
		{@const max = isMaxTokens ? maxTokens : GENERATION_CONFIG_SETTINGS[key].max}

		<div>
			<div class="flex items-center justify-between">
				<label for={key} class="mb-0.5 block text-sm font-medium text-gray-900 dark:text-white">
					{label}
				</label>
				<div class="flex items-center gap-2">
					{#if !isMaxTokens || isNumber(conversation.data.config[key])}
						<input
							type="number"
							class="w-20 rounded-sm border bg-transparent px-1 py-0.5 text-right text-sm dark:border-gray-700"
							{min}
							{max}
							{step}
							bind:value={() => conversation.data.config[key], v => updateConfigKey(key, v)}
						/>
					{/if}
					{#if isMaxTokens && isNumber(conversation.data.config[key])}
						<button class="btn-mini" onclick={() => updateConfigKey(key, undefined)}> <IconX /> </button>
					{:else if isMaxTokens}
						<button class="btn-mini" onclick={() => updateConfigKey(key, maxTokens / 2)}> set </button>
					{/if}
				</div>
			</div>
			{#if !isMaxTokens || isNumber(conversation.data.config[key])}
				<input
					id={key}
					type="range"
					{min}
					{max}
					{step}
					bind:value={() => conversation.data.config[key], v => updateConfigKey(key, v)}
					class="h-2 w-full cursor-pointer appearance-none rounded-lg bg-gray-200 accent-black dark:bg-gray-700 dark:accent-blue-500"
				/>
			{/if}
		</div>
	{/each}

	<label class="mt-2 flex cursor-pointer items-center justify-between">
		<input
			type="checkbox"
			bind:checked={() => conversation.data.streaming, v => conversation.update({ streaming: v })}
			class="peer sr-only"
		/>
		<span class="text-sm font-medium text-gray-900 dark:text-gray-300">Streaming</span>
		<div
			class="peer relative h-5 w-9 rounded-full bg-gray-200 peer-checked:bg-black peer-focus:outline-hidden after:absolute after:start-[2px] after:top-[2px] after:h-4 after:w-4 after:rounded-full after:border after:border-gray-300 after:bg-white after:transition-all after:content-[''] peer-checked:after:translate-x-full peer-checked:after:border-white dark:border-gray-600 dark:bg-gray-700 dark:peer-checked:bg-blue-600"
		></div>
	</label>

	<!-- eslint-disable-next-line @typescript-eslint/no-explicit-any -->
	{#if conversation.isStructuredOutputAllowed}
		<label class="mt-2 flex cursor-pointer items-center justify-between" for="structured-output">
			<span class="text-sm font-medium text-gray-900 dark:text-gray-300">Structured Output</span>
			<div class="flex items-center gap-2">
				<input
					type="checkbox"
					bind:checked={
						() => conversation.data.structuredOutput?.enabled,
						v =>
							conversation.update({ structuredOutput: { ...conversation.data.structuredOutput, enabled: v ?? false } })
					}
					class="peer sr-only"
					id="structured-output"
				/>
				<button class="btn-mini" type="button" onclick={openStructuredOutputModal}> edit </button>
				<div
					class="peer relative h-5 w-9 rounded-full bg-gray-200 peer-checked:bg-black peer-focus:outline-hidden after:absolute after:start-[2px] after:top-[2px] after:h-4 after:w-4 after:rounded-full after:border after:border-gray-300 after:bg-white after:transition-all after:content-[''] peer-checked:after:translate-x-full peer-checked:after:border-white dark:border-gray-600 dark:bg-gray-700 dark:peer-checked:bg-blue-600"
				></div>
			</div>
		</label>
	{/if}

	<!-- MCP Servers -->
	{#if isMcpEnabled()}
		<div class="mt-2 flex cursor-pointer items-center justify-between">
			<span class="text-sm font-medium text-gray-900 dark:text-gray-300">MCP Servers</span>
			<div class="flex items-center gap-2">
				{#if mcpServers.enabled.length > 0}
					<span class="rounded-full bg-blue-100 px-2 py-1 text-xs text-blue-800 dark:bg-blue-900 dark:text-blue-200">
						{mcpServers.enabled.length} enabled
					</span>
				{/if}
				<button class="btn-mini" type="button" onclick={() => (editingMCP = true)}> configure </button>
			</div>
		</div>
	{/if}

	<div class="mt-2 flex items-center gap-2">
		<span class="text-sm font-medium text-gray-900 dark:text-gray-300">Extra parameters</span>
		<span
			class={cn(
				"rounded-md bg-black px-2 py-1 text-xs font-semibold text-white dark:bg-blue-600",
				!extraParamsLen && "hidden",
			)}
		>
			{extraParamsLen}
		</span>
		<button class="btn-mini ml-auto" type="button" onclick={openExtraParamsModal}>edit</button>
	</div>

	<label class="mt-2 flex cursor-pointer items-center justify-between">
		<input
			type="checkbox"
			bind:checked={() => conversation.data.parseMarkdown, v => conversation.update({ parseMarkdown: v })}
			class="peer sr-only"
		/>
		<span class="text-sm font-medium text-gray-900 dark:text-gray-300">Parse Markdown</span>
		<div
			class="peer relative h-5 w-9 rounded-full bg-gray-200 peer-checked:bg-black peer-focus:outline-hidden after:absolute after:start-[2px] after:top-[2px] after:h-4 after:w-4 after:rounded-full after:border after:border-gray-300 after:bg-white after:transition-all after:content-[''] peer-checked:after:translate-x-full peer-checked:after:border-white dark:border-gray-600 dark:bg-gray-700 dark:peer-checked:bg-blue-600"
		></div>
	</label>
</div>

<StructuredOutputModal {conversation} />
<ExtraParamsModal {conversation} />
{#if isMcpEnabled()}
	<MCPModal bind:open={editingMCP} />
{/if}