Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 1 addition & 13 deletions src/lib/sessionSchema/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,6 @@ export const toPayload = async (server: CoralServer, data: z.output<FormSchema>)
} satisfies CreateSessionRequest;
};

// export const defaultPayload = {
// groups: [],
// tools: {},
// sessionRuntimeSettings: {
// ttl: 50000
// },
// agents: []
// } satisfies CreateSessionRequest;

export const defaultProvider = {
runtime: 'executable',
remote_request: {
Expand Down Expand Up @@ -139,14 +130,11 @@ export const importFromPayload = (json: string): z.output<FormSchema> => {
];
})
);
const defaultRuntimeSettings = {
ttl: 50000
};

return {
tools,
groups: data.agentGraphRequest.groups ?? [],
sessionRuntimeSettings: {
...defaultRuntimeSettings,
...(data.execution && data.execution.mode === 'immediate'
? data.execution.runtimeSettings
: {})
Expand Down
2 changes: 1 addition & 1 deletion src/lib/sessionSchema/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ export type CustomTool = z.output<typeof CustomToolSchema>;

const formSchema = z.object({
sessionRuntimeSettings: z.object({
ttl: z.number().min(10000).max(15778476000).default(900000)
ttl: z.number().min(10000).max(15778476000).optional()
}),
sessionBudgetSettings: z.object({
budget: z.number().min(10000).default(100000000),
Expand Down
140 changes: 140 additions & 0 deletions src/routes/(app)/workbench/options/DurationInout.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
<script lang="ts">
import * as ButtonGroup from '@coral-os/component-library/ui/button-group/index.js';
import * as InputGroup from '@coral-os/component-library/ui/input-group/index.js';
import * as Label from '@coral-os/component-library/ui/label/index.js';
import { tick } from 'svelte';

interface Props {
value: number;
disabled?: boolean;
onchange?: (totalMilliseconds: number) => void;
}

let { value, disabled = false, onchange }: Props = $props();

const toHMS = (totalMs: number) => {
const totalSeconds = Math.floor(totalMs / 1000);
return {
h: Math.floor(totalSeconds / 3600),
m: Math.floor((totalSeconds % 3600) / 60),
s: totalSeconds % 60
};
};

const toSeconds = (h: number, m: number, s: number) =>
(h * 3600 + Math.min(m, 59) * 60 + Math.min(s, 59)) * 1000;

const pad = (n: number) => String(n).padStart(2, '0');
const parseSegment = (v: string) => {
const n = parseInt(v.replace(/\D/g, ''), 10);
return isNaN(n) ? 0 : n;
};

let isEditingH = $state(false);
let isEditingM = $state(false);
let isEditingS = $state(false);
let editH = $state('');
let editM = $state('');
let editS = $state('');

const hms = $derived(toHMS(value ?? 0));

async function onFocusSegment(
e: FocusEvent & { currentTarget: HTMLInputElement },
segment: 'h' | 'm' | 's'
) {
if (segment === 'h') {
isEditingH = true;
editH = String(hms.h);
}
if (segment === 'm') {
isEditingM = true;
editM = String(hms.m);
}
if (segment === 's') {
isEditingS = true;
editS = String(hms.s);
}
await tick();
e.currentTarget.select();
}

function onBlurSegment(
e: FocusEvent & { currentTarget: HTMLInputElement },
segment: 'h' | 'm' | 's'
) {
const raw = parseSegment(e.currentTarget.value);
if (segment === 'h') {
isEditingH = false;
}
if (segment === 'm') {
isEditingM = false;
}
if (segment === 's') {
isEditingS = false;
}
const { h, m, s } = hms;
const next = {
h: segment === 'h' ? raw : h,
m: segment === 'm' ? raw : m,
s: segment === 's' ? raw : s
};
onchange?.(toSeconds(next.h, next.m, next.s));
}
</script>

<ButtonGroup.Root class="grow">
<!-- Hours -->
<ButtonGroup.Text>
<Label.Root>Hours</Label.Root>
</ButtonGroup.Text>
<InputGroup.Root>
<InputGroup.Input
value={isEditingH ? editH : String(hms.h)}
onfocus={(e) => onFocusSegment(e, 'h')}
onblur={(e) => onBlurSegment(e, 'h')}
oninput={(e) => (editH = e.currentTarget.value)}
{disabled}
placeholder="0"
maxlength={4}
inputmode="numeric"
type="text"
/>
</InputGroup.Root>

<!-- Minutes -->
<ButtonGroup.Text>
<Label.Root>Minutes</Label.Root>
</ButtonGroup.Text>
<InputGroup.Root>
<InputGroup.Input
value={isEditingM ? editM : pad(hms.m)}
onfocus={(e) => onFocusSegment(e, 'm')}
onblur={(e) => onBlurSegment(e, 'm')}
oninput={(e) => (editM = e.currentTarget.value)}
{disabled}
placeholder="00"
maxlength={2}
inputmode="numeric"
type="text"
/>
</InputGroup.Root>

<!-- Seconds -->
<ButtonGroup.Text>
<Label.Root>Seconds</Label.Root>
</ButtonGroup.Text>
<InputGroup.Root>
<InputGroup.Input
value={isEditingS ? editS : pad(hms.s)}
onfocus={(e) => onFocusSegment(e, 's')}
onblur={(e) => onBlurSegment(e, 's')}
oninput={(e) => (editS = e.currentTarget.value)}
{disabled}
placeholder="00"
maxlength={2}
inputmode="numeric"
type="text"
/>
</InputGroup.Root>
</ButtonGroup.Root>
29 changes: 14 additions & 15 deletions src/routes/(app)/workbench/panes/SessionPane.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import { randomAdjective, randomAnimal } from '$lib/words';
import { getSessionContext } from '$lib/sessionCreatorContext';
import { cn } from '$lib/utils';
import DurationInout from '../options/DurationInout.svelte';

let ctx = getSessionContext();

Expand All @@ -48,38 +49,36 @@

{#if ctx && $formData}
<section class="flex h-full min-h-0 grow flex-col gap-2 overflow-y-auto p-4">
<p>Time to live restricts how long a session can remain alive and open for.</p>
<p>If specified, the session will never live longer than this duration.</p>
<Form.ElementField {form} name="sessionRuntimeSettings.ttl" class="flex items-center gap-2 ">
<Form.Control>
{#snippet children({ props })}
<TooltipLabel
title="Time to live (TTL)"
tooltip="Measured in milliseconds, the time to live is the maximum duration the session can last"
extra={{
required: true,
type: 'number'
}}
class="max-w-1/4 min-w-1/4"
>
Time to live
</TooltipLabel>
<Input
{...props}
bind:value={$formData.sessionRuntimeSettings.ttl}
placeholder="time in milliseconds"
maxlength={15778476000}
type="number"
class="grow"

<DurationInout
value={$formData.sessionRuntimeSettings.ttl ?? 0}
onchange={(totalMilliseconds) => {
if (totalMilliseconds === 0) {
delete $formData.sessionRuntimeSettings.ttl;
$formData.sessionBudgetSettings = $formData.sessionBudgetSettings;
} else {
$formData.sessionRuntimeSettings.ttl = totalMilliseconds;
}
}}
/>
{/snippet}
</Form.Control>
</Form.ElementField>
<span class="text-muted-foreground flex flex-col justify-between">
<TooltipLabel tooltip="Based off Session time to live settings" class=" max-w-fit">
Maximum session duration: {formatMsToHHMMSS($formData.sessionRuntimeSettings.ttl ?? 0) ??
'HH:MM:SS'}
</TooltipLabel>
</span>

{#if $errors?.sessionRuntimeSettings?.ttl && JSON.stringify($errors.sessionRuntimeSettings?.ttl) !== '{}' && JSON.stringify($errors.sessionRuntimeSettings?.ttl) !== '{}'}
<span class="text-xs">
{$errors?.sessionRuntimeSettings?.ttl}
Expand Down
Loading