Skip to content

Commit f6359aa

Browse files
chase-crumbaughqstearns
authored andcommitted
feat: support variations on any tool type (#577)
Introduces support for "varying" any tool type. Concretely, customTool names will now be editable (previously they could not be changed once created)
1 parent 10508ca commit f6359aa

File tree

37 files changed

+1540
-1114
lines changed

37 files changed

+1540
-1114
lines changed

.changeset/friendly-shirts-poke.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@gram/dashboard": patch
3+
"@gram/client": patch
4+
"@gram/server": patch
5+
---
6+
7+
Support variations on any tool type. Allows the names of Custom Tools to now be edited along with all fields of Functions.

.speakeasy/out.openapi.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10530,6 +10530,9 @@ components:
1053010530
enum:
1053110531
- prompt
1053210532
- higher_order_tool
10533+
name:
10534+
type: string
10535+
description: The name of the prompt template. Will be updated via variation
1053310536
prompt:
1053410537
type: string
1053510538
description: The template content

client/dashboard/src/pages/playground/ChatWindow.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,13 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3030
import { v7 as uuidv7 } from "uuid";
3131
import { z } from "zod";
3232
import { onboardingStepStorageKeys } from "../home/Home";
33+
import { ChatComposerWrapper } from "./ChatComposerWrapper";
3334
import { useChatContext } from "./ChatContext";
3435
import { useChatHistory } from "./ChatHistory";
3536
import { MessageHistoryIndicator } from "./MessageHistoryIndicator";
3637
import { useMiniModel, useModel } from "./Openrouter";
38+
import { Tool as MentionTool, parseMentionedTools } from "./ToolMentions";
3739
import { useMessageHistoryNavigation } from "./useMessageHistoryNavigation";
38-
import { parseMentionedTools, Tool as MentionTool } from "./ToolMentions";
39-
import { ChatComposerWrapper } from "./ChatComposerWrapper";
4040

4141
const defaultModel = {
4242
label: "Claude 4.5 Sonnet",
@@ -72,12 +72,14 @@ export function ChatWindow({
7272
additionalActions,
7373
initialMessages,
7474
initialPrompt,
75+
hideTemperatureSlider = false,
7576
}: {
7677
configRef: ChatConfig;
7778
dynamicToolset?: boolean;
7879
additionalActions?: React.ReactNode;
7980
initialMessages?: Message[];
8081
initialPrompt?: string | null;
82+
hideTemperatureSlider?: boolean;
8183
}) {
8284
const [model, setModel] = useState(defaultModel.value);
8385
const [temperature, setTemperature] = useState(0.5);
@@ -89,13 +91,12 @@ export function ChatWindow({
8991
key={chatKey}
9092
model={model}
9193
setModel={setModel}
92-
temperature={temperature}
93-
setTemperature={setTemperature}
9494
configRef={configRef}
9595
dynamicToolset={dynamicToolset}
9696
initialMessages={initialMessages}
9797
additionalActions={additionalActions}
9898
initialPrompt={initialPrompt}
99+
{...(!hideTemperatureSlider && { temperature, setTemperature })}
99100
/>
100101
);
101102
}
@@ -118,8 +119,8 @@ function ChatInner({
118119
}: {
119120
model: string;
120121
setModel: (model: string) => void;
121-
temperature: number;
122-
setTemperature: (temperature: number) => void;
122+
temperature?: number;
123+
setTemperature?: (temperature: number) => void;
123124
configRef: ChatConfig;
124125
dynamicToolset: boolean;
125126
initialMessages?: Message[];
@@ -577,7 +578,7 @@ function ChatInner({
577578
/* eslint-disable @typescript-eslint/no-explicit-any */
578579
const m = messagesToDisplay as any;
579580

580-
const temperatureSlider = (
581+
const temperatureSlider = temperature && setTemperature && (
581582
<div className="flex items-center gap-3 px-2">
582583
<SimpleTooltip tooltip="Controls randomness in responses. Lower values (0.0-0.3) make outputs more focused and deterministic. Higher values (0.7-1.0) increase creativity and variety. Default: 0.5">
583584
<span className="text-xs text-muted-foreground whitespace-nowrap cursor-help">

client/dashboard/src/pages/toolBuilder/CustomTools.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ export function CustomToolCard({ template }: { template: PromptTemplate }) {
9999

100100
return (
101101
<routes.customTools.toolBuilder.Link
102-
params={[template.name]}
102+
params={[template.canonicalName]}
103103
className="hover:no-underline"
104104
>
105105
<Card>

client/dashboard/src/pages/toolBuilder/ToolBuilder.tsx

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { SimpleTooltip } from "@/components/ui/tooltip";
2222
import { Type } from "@/components/ui/type";
2323
import { useSdkClient } from "@/contexts/Sdk";
2424
import { useTelemetry } from "@/contexts/Telemetry";
25+
import { useToolset } from "@/hooks/toolTypes";
2526
import { useApiError } from "@/hooks/useApiError";
2627
import { MUSTACHE_VAR_REGEX, slugify, TOOL_NAME_REGEX } from "@/lib/constants";
2728
import { Tool, useGroupedTools } from "@/lib/toolTypes";
@@ -33,7 +34,8 @@ import {
3334
} from "@gram/client/models/components";
3435
import {
3536
invalidateAllListToolsets,
36-
invalidateTemplate,
37+
invalidateAllTemplate,
38+
invalidateAllTemplates,
3739
useListToolsetsSuspense,
3840
useTemplateSuspense,
3941
useUpdateTemplateMutation,
@@ -49,7 +51,6 @@ import { ChatProvider, useChatContext } from "../playground/ChatContext";
4951
import { ChatConfig, ChatWindow } from "../playground/ChatWindow";
5052
import { ToolsetDropdown } from "../toolsets/ToolsetDropown";
5153
import { useToolifyContext } from "./Toolify";
52-
import { useToolset } from "@/hooks/toolTypes";
5354

5455
type Input = {
5556
name: string;
@@ -334,6 +335,7 @@ function ToolBuilder({ initial }: { initial: ToolBuilderState }) {
334335
};
335336

336337
const anyChanges =
338+
name !== initial.name ||
337339
description !== initial.description ||
338340
purpose !== initial.purpose ||
339341
inputs.length !== initial.inputs.length ||
@@ -367,7 +369,8 @@ function ToolBuilder({ initial }: { initial: ToolBuilderState }) {
367369

368370
const { mutate: updatePrompt } = useUpdateTemplateMutation({
369371
onSettled: () => {
370-
invalidateTemplate(queryClient, [{ name }]);
372+
invalidateAllTemplate(queryClient);
373+
invalidateAllTemplates(queryClient);
371374
},
372375
onError: (error) => {
373376
handleApiError(error, "Failed to update tool");
@@ -407,6 +410,7 @@ function ToolBuilder({ initial }: { initial: ToolBuilderState }) {
407410
request: {
408411
updatePromptTemplateForm: {
409412
id: initial.id,
413+
name,
410414
description,
411415
prompt: higherOrderToolToJSON(higherOrderTool),
412416
arguments: JSON.stringify(argsJsonSchema),
@@ -463,22 +467,13 @@ function ToolBuilder({ initial }: { initial: ToolBuilderState }) {
463467
</Button>
464468
);
465469

466-
const toolName = initial.id ? (
467-
<Heading
468-
variant="h3"
469-
className={cn("normal-case w-fit", initial.id && "text-muted-foreground")}
470-
tooltip="Can't change name after tool is created"
471-
>
472-
{name}
473-
</Heading>
474-
) : (
470+
const toolName = (
475471
<EditableText
476472
label="Tool Name"
477473
description="Give your tool a name. This influences tool selection."
478474
value={name}
479475
onSubmit={setName}
480476
validate={validateName}
481-
disabled={!!initial.id}
482477
>
483478
<Heading variant="h3" className="normal-case">
484479
{name}
@@ -1098,6 +1093,7 @@ function ChatPanel(props: {
10981093
configRef={chatConfigRef}
10991094
additionalActions={additionalActions}
11001095
initialMessages={customToolSystemPrompt}
1096+
hideTemperatureSlider
11011097
/>
11021098
);
11031099
}

client/dashboard/src/pages/toolsets/Toolset.tsx

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
1+
import { InputDialog } from "@/components/input-dialog";
12
import { Page } from "@/components/page-layout";
3+
import { ToolList } from "@/components/tool-list";
4+
import { Dialog } from "@/components/ui/dialog";
25
import { Heading } from "@/components/ui/heading";
36
import { MoreActions } from "@/components/ui/more-actions";
47
import { MultiSelect } from "@/components/ui/multi-select";
58
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
9+
import { useCommandPalette } from "@/contexts/CommandPalette";
10+
import { useSdkClient } from "@/contexts/Sdk";
611
import {
712
useRegisterEnvironmentTelemetry,
813
useRegisterToolsetTelemetry,
914
useTelemetry,
1015
} from "@/contexts/Telemetry";
16+
import { useToolset } from "@/hooks/toolTypes";
1117
import { useApiError } from "@/hooks/useApiError";
12-
import { useGroupedTools } from "@/lib/toolTypes";
18+
import { Tool, useGroupedTools } from "@/lib/toolTypes";
1319
import { cn } from "@/lib/utils";
1420
import { useRoutes } from "@/routes";
21+
import { Confirm } from "@gram/client/models/components";
22+
import { invalidateTemplate } from "@gram/client/react-query";
1523
import {
1624
queryKeyInstance,
1725
useCloneToolsetMutation,
@@ -21,26 +29,17 @@ import {
2129
} from "@gram/client/react-query/index.js";
2230
import { Button, Icon, Stack } from "@speakeasy-api/moonshine";
2331
import { useQueryClient } from "@tanstack/react-query";
24-
import { useState, useEffect, useCallback } from "react";
32+
import { useCallback, useEffect, useState } from "react";
2533
import { Outlet, useParams } from "react-router";
2634
import { toast } from "sonner";
2735
import { MCPDetails, MCPEnableButton } from "../mcp/MCPDetails";
36+
import { AddToolsDialog } from "./AddToolsDialog";
2837
import { PromptsTabContent } from "./PromptsTab";
2938
import { ToolsetAuth } from "./ToolsetAuth";
3039
import { ToolsetAuthAlert } from "./ToolsetAuthAlert";
3140
import { ToolsetEmptyState } from "./ToolsetEmptyState";
3241
import { ToolsetHeader } from "./ToolsetHeader";
3342
import { useToolsets } from "./Toolsets";
34-
import { useToolset } from "@/hooks/toolTypes";
35-
import { Tool } from "@/lib/toolTypes";
36-
import { ToolList } from "@/components/tool-list";
37-
import { useSdkClient } from "@/contexts/Sdk";
38-
import { Confirm } from "@gram/client/models/components";
39-
import { invalidateTemplate } from "@gram/client/react-query";
40-
import { InputDialog } from "@/components/input-dialog";
41-
import { Dialog } from "@/components/ui/dialog";
42-
import { AddToolsDialog } from "./AddToolsDialog";
43-
import { useCommandPalette } from "@/contexts/CommandPalette";
4443

4544
export function useDeleteToolset({
4645
onSuccess,
@@ -356,7 +355,16 @@ export function ToolsetView({
356355
tool: Tool,
357356
updates: { name?: string; description?: string },
358357
) => {
359-
if (tool.type === "http") {
358+
if (tool.type === "prompt") {
359+
// Prompt templates are only sometimes updated via variation under the hood. We use this endpoint instead to ensure it mirrors editing from the ToolBuilder page itself.
360+
await client.templates.update({
361+
updatePromptTemplateForm: {
362+
...tool,
363+
...updates,
364+
},
365+
});
366+
invalidateTemplate(queryClient, [{ name: tool.name }]);
367+
} else {
360368
await client.variations.upsertGlobal({
361369
upsertGlobalToolVariationForm: {
362370
...tool.variation,
@@ -366,14 +374,6 @@ export function ToolsetView({
366374
srcToolUrn: tool.toolUrn,
367375
},
368376
});
369-
} else {
370-
await client.templates.update({
371-
updatePromptTemplateForm: {
372-
...tool,
373-
...updates,
374-
},
375-
});
376-
invalidateTemplate(queryClient, [{ name: tool.name }]);
377377
}
378378

379379
telemetry.capture("toolset_event", {

client/sdk/.speakeasy/gen.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
lockVersion: 2.0.0
22
id: 0e7a6274-2092-40cd-9586-9415c6655c64
33
management:
4-
docChecksum: a3a1926b815df1d99ef053f0a00c1b5f
4+
docChecksum: 13e7bf4996e35ee26f79c969348fc37c
55
docVersion: 0.0.1
66
speakeasyVersion: 1.613.0
77
generationVersion: 2.696.0
8-
releaseVersion: 0.14.9
9-
configChecksum: 7cc0154927ec14183e72e088b3df6547
8+
releaseVersion: 0.14.10
9+
configChecksum: a4aacac5181191246bed8d5b4b80cfc1
1010
features:
1111
typescript:
1212
additionalDependencies: 0.1.0

client/sdk/.speakeasy/gen.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ generation:
2525
generateNewTests: false
2626
skipResponseBodyAssertions: false
2727
typescript:
28-
version: 0.14.9
28+
version: 0.14.10
2929
acceptHeaderEnum: true
3030
additionalDependencies:
3131
dependencies: {}

client/sdk/docs/models/components/updateprompttemplateform.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ let value: UpdatePromptTemplateForm = {
1919
| `engine` | [components.UpdatePromptTemplateFormEngine](../../models/components/updateprompttemplateformengine.md) | :heavy_minus_sign: | The template engine |
2020
| `id` | *string* | :heavy_check_mark: | The ID of the prompt template to update |
2121
| `kind` | [components.UpdatePromptTemplateFormKind](../../models/components/updateprompttemplateformkind.md) | :heavy_minus_sign: | The kind of prompt the template is used for |
22+
| `name` | *string* | :heavy_minus_sign: | The name of the prompt template. Will be updated via variation |
2223
| `prompt` | *string* | :heavy_minus_sign: | The template content |
2324
| `toolsHint` | *string*[] | :heavy_minus_sign: | The suggested tool names associated with the prompt template |

client/sdk/jsr.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
{
44
"name": "@gram/client",
5-
"version": "0.14.9",
5+
"version": "0.14.10",
66
"exports": {
77
".": "./src/index.ts",
88
"./models/errors": "./src/models/errors/index.ts",

0 commit comments

Comments
 (0)