mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-06-22 08:38:13 +02:00
feat: add openai realtime models
This commit is contained in:
parent
53f1959edf
commit
4d7b681928
33 changed files with 1518 additions and 75 deletions
|
|
@ -11,6 +11,8 @@ import {
|
|||
KeyValueEditor,
|
||||
type KeyValueItem,
|
||||
ParameterEditor,
|
||||
PresetParameterEditor,
|
||||
type PresetToolParameter,
|
||||
type ToolParameter,
|
||||
UrlInput,
|
||||
} from "@/components/http";
|
||||
|
|
@ -35,6 +37,8 @@ export interface HttpApiToolConfigProps {
|
|||
onHeadersChange: (headers: KeyValueItem[]) => void;
|
||||
parameters: ToolParameter[];
|
||||
onParametersChange: (parameters: ToolParameter[]) => void;
|
||||
presetParameters: PresetToolParameter[];
|
||||
onPresetParametersChange: (parameters: PresetToolParameter[]) => void;
|
||||
timeoutMs: number;
|
||||
onTimeoutMsChange: (timeout: number) => void;
|
||||
customMessage: string;
|
||||
|
|
@ -61,6 +65,8 @@ export function HttpApiToolConfig({
|
|||
onHeadersChange,
|
||||
parameters,
|
||||
onParametersChange,
|
||||
presetParameters,
|
||||
onPresetParametersChange,
|
||||
timeoutMs,
|
||||
onTimeoutMsChange,
|
||||
customMessage,
|
||||
|
|
@ -182,7 +188,7 @@ export function HttpApiToolConfig({
|
|||
|
||||
<TabsContent value="parameters" className="space-y-4 mt-4">
|
||||
<div className="grid gap-2">
|
||||
<Label>Tool Parameters</Label>
|
||||
<Label>LLM Parameters</Label>
|
||||
<Label className="text-xs text-muted-foreground">
|
||||
Define the parameters that the LLM will provide when calling this tool.
|
||||
These will be sent as JSON body for POST/PUT/PATCH or as URL query params for GET/DELETE.
|
||||
|
|
@ -193,6 +199,18 @@ export function HttpApiToolConfig({
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2 pt-4 border-t">
|
||||
<Label>Preset Parameters</Label>
|
||||
<Label className="text-xs text-muted-foreground">
|
||||
Add values that Dograh should inject at runtime. These are not exposed to the LLM and can use
|
||||
workflow templates like {`{{initial_context.phone_number}}`} or fixed literals.
|
||||
</Label>
|
||||
<PresetParameterEditor
|
||||
parameters={presetParameters}
|
||||
onChange={onPresetParametersChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2 pt-4 border-t">
|
||||
<Label>Custom Headers</Label>
|
||||
<Label className="text-xs text-muted-foreground">
|
||||
|
|
|
|||
|
|
@ -11,7 +11,13 @@ import {
|
|||
} from "@/client/sdk.gen";
|
||||
import type { RecordingResponseSchema, ToolResponse, TransferCallConfig as APITransferCallConfig } from "@/client/types.gen";
|
||||
import type { EndCallConfig } from "@/client/types.gen";
|
||||
import { type HttpMethod, type KeyValueItem, type ToolParameter, validateUrl } from "@/components/http";
|
||||
import {
|
||||
type HttpMethod,
|
||||
type KeyValueItem,
|
||||
type PresetToolParameter,
|
||||
type ToolParameter,
|
||||
validateUrl,
|
||||
} from "@/components/http";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
|
|
@ -41,6 +47,12 @@ interface HttpApiConfigWithParams {
|
|||
headers?: Record<string, string>;
|
||||
credential_uuid?: string;
|
||||
parameters?: ToolParameter[];
|
||||
preset_parameters?: Array<{
|
||||
name?: string;
|
||||
type?: PresetToolParameter["type"];
|
||||
value_template?: string;
|
||||
required?: boolean;
|
||||
}>;
|
||||
timeout_ms?: number;
|
||||
customMessage?: string;
|
||||
}
|
||||
|
|
@ -70,6 +82,7 @@ export default function ToolDetailPage() {
|
|||
const [credentialUuid, setCredentialUuid] = useState("");
|
||||
const [headers, setHeaders] = useState<KeyValueItem[]>([]);
|
||||
const [parameters, setParameters] = useState<ToolParameter[]>([]);
|
||||
const [presetParameters, setPresetParameters] = useState<PresetToolParameter[]>([]);
|
||||
const [timeoutMs, setTimeoutMs] = useState(5000);
|
||||
|
||||
// End Call form state
|
||||
|
|
@ -209,6 +222,19 @@ export default function ToolDetailPage() {
|
|||
} else {
|
||||
setParameters([]);
|
||||
}
|
||||
|
||||
if (config.preset_parameters && Array.isArray(config.preset_parameters)) {
|
||||
setPresetParameters(
|
||||
config.preset_parameters.map((p) => ({
|
||||
name: p.name || "",
|
||||
type: p.type || "string",
|
||||
valueTemplate: p.value_template || "",
|
||||
required: p.required ?? true,
|
||||
}))
|
||||
);
|
||||
} else {
|
||||
setPresetParameters([]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -263,6 +289,14 @@ export default function ToolDetailPage() {
|
|||
setError("All parameters must have a name");
|
||||
return;
|
||||
}
|
||||
|
||||
const invalidPresetParams = presetParameters.filter(
|
||||
(p) => !p.name.trim() || !p.valueTemplate.trim()
|
||||
);
|
||||
if (invalidPresetParams.length > 0) {
|
||||
setError("All preset parameters must have a name and a value");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
@ -325,6 +359,9 @@ export default function ToolDetailPage() {
|
|||
});
|
||||
|
||||
const validParameters = parameters.filter((p) => p.name.trim());
|
||||
const validPresetParameters = presetParameters.filter(
|
||||
(p) => p.name.trim() && p.valueTemplate.trim()
|
||||
);
|
||||
|
||||
requestBody = {
|
||||
name,
|
||||
|
|
@ -342,6 +379,15 @@ export default function ToolDetailPage() {
|
|||
: undefined,
|
||||
parameters:
|
||||
validParameters.length > 0 ? validParameters : undefined,
|
||||
preset_parameters:
|
||||
validPresetParameters.length > 0
|
||||
? validPresetParameters.map((p) => ({
|
||||
name: p.name,
|
||||
type: p.type,
|
||||
value_template: p.valueTemplate,
|
||||
required: p.required,
|
||||
}))
|
||||
: undefined,
|
||||
timeout_ms: timeoutMs,
|
||||
customMessage: customMessageType === 'text' ? (customMessage || undefined) : undefined,
|
||||
customMessageType,
|
||||
|
|
@ -394,8 +440,20 @@ export default function ToolDetailPage() {
|
|||
exampleBody[p.name] = `<${p.name}>`;
|
||||
}
|
||||
});
|
||||
presetParameters.forEach((p) => {
|
||||
if (p.type === "number") {
|
||||
exampleBody[p.name] = p.valueTemplate || 0;
|
||||
} else if (p.type === "boolean") {
|
||||
exampleBody[p.name] = p.valueTemplate || true;
|
||||
} else {
|
||||
exampleBody[p.name] = p.valueTemplate || `<${p.name}>`;
|
||||
}
|
||||
});
|
||||
|
||||
const hasBody = httpMethod !== "GET" && httpMethod !== "DELETE" && parameters.length > 0;
|
||||
const hasBody =
|
||||
httpMethod !== "GET" &&
|
||||
httpMethod !== "DELETE" &&
|
||||
(parameters.length > 0 || presetParameters.length > 0);
|
||||
|
||||
return `// ${tool.name}
|
||||
// ${tool.description || "HTTP API Tool"}
|
||||
|
|
@ -571,6 +629,8 @@ const data = await response.json();`;
|
|||
onHeadersChange={setHeaders}
|
||||
parameters={parameters}
|
||||
onParametersChange={setParameters}
|
||||
presetParameters={presetParameters}
|
||||
onPresetParametersChange={setPresetParameters}
|
||||
timeoutMs={timeoutMs}
|
||||
onTimeoutMsChange={setTimeoutMs}
|
||||
customMessage={customMessage}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1860,6 +1860,12 @@ export type HttpApiConfig = {
|
|||
* Parameters that the tool accepts from LLM
|
||||
*/
|
||||
parameters?: Array<ToolParameter> | null;
|
||||
/**
|
||||
* Preset Parameters
|
||||
*
|
||||
* Parameters injected by Dograh from fixed values or workflow context templates
|
||||
*/
|
||||
preset_parameters?: Array<PresetToolParameter> | null;
|
||||
/**
|
||||
* Timeout Ms
|
||||
*
|
||||
|
|
@ -2480,6 +2486,38 @@ export type PlivoConfigurationResponse = {
|
|||
from_numbers: Array<string>;
|
||||
};
|
||||
|
||||
/**
|
||||
* PresetToolParameter
|
||||
*
|
||||
* A parameter injected by Dograh at runtime.
|
||||
*/
|
||||
export type PresetToolParameter = {
|
||||
/**
|
||||
* Name
|
||||
*
|
||||
* Parameter name (used as key in request body)
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Type
|
||||
*
|
||||
* Parameter type: string, number, or boolean
|
||||
*/
|
||||
type: string;
|
||||
/**
|
||||
* Value Template
|
||||
*
|
||||
* Fixed value or template, e.g. {{initial_context.phone_number}}
|
||||
*/
|
||||
value_template: string;
|
||||
/**
|
||||
* Required
|
||||
*
|
||||
* Whether the parameter must resolve to a non-empty value
|
||||
*/
|
||||
required?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* PresignedUploadUrlRequest
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -2,5 +2,11 @@ export { CreateCredentialDialog } from "./create-credential-dialog";
|
|||
export { CredentialSelector } from "./credential-selector";
|
||||
export { type HttpMethod, HttpMethodSelector } from "./http-method-selector";
|
||||
export { KeyValueEditor, type KeyValueItem } from "./key-value-editor";
|
||||
export { ParameterEditor, type ParameterType,type ToolParameter } from "./parameter-editor";
|
||||
export { UrlInput, type UrlValidationResult,validateUrl } from "./url-input";
|
||||
export {
|
||||
ParameterEditor,
|
||||
type ParameterType,
|
||||
PresetParameterEditor,
|
||||
type PresetToolParameter,
|
||||
type ToolParameter,
|
||||
} from "./parameter-editor";
|
||||
export { UrlInput, type UrlValidationResult, validateUrl } from "./url-input";
|
||||
|
|
|
|||
|
|
@ -23,6 +23,13 @@ export interface ToolParameter {
|
|||
required: boolean;
|
||||
}
|
||||
|
||||
export interface PresetToolParameter {
|
||||
name: string;
|
||||
type: ParameterType;
|
||||
valueTemplate: string;
|
||||
required: boolean;
|
||||
}
|
||||
|
||||
interface ParameterEditorProps {
|
||||
parameters: ToolParameter[];
|
||||
onChange: (parameters: ToolParameter[]) => void;
|
||||
|
|
@ -165,3 +172,146 @@ export function ParameterEditor({
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface PresetParameterEditorProps {
|
||||
parameters: PresetToolParameter[];
|
||||
onChange: (parameters: PresetToolParameter[]) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export function PresetParameterEditor({
|
||||
parameters,
|
||||
onChange,
|
||||
disabled = false,
|
||||
}: PresetParameterEditorProps) {
|
||||
const addParameter = () => {
|
||||
onChange([
|
||||
...parameters,
|
||||
{ name: "", type: "string", valueTemplate: "", required: true },
|
||||
]);
|
||||
};
|
||||
|
||||
const updateParameter = (
|
||||
index: number,
|
||||
field: keyof PresetToolParameter,
|
||||
value: string | boolean
|
||||
) => {
|
||||
const newParams = [...parameters];
|
||||
newParams[index] = { ...newParams[index], [field]: value };
|
||||
onChange(newParams);
|
||||
};
|
||||
|
||||
const removeParameter = (index: number) => {
|
||||
onChange(parameters.filter((_, i) => i !== index));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{parameters.length === 0 && (
|
||||
<div className="text-sm text-muted-foreground py-4 text-center border border-dashed rounded-md">
|
||||
No preset parameters defined. Add one to inject a fixed value or workflow context into the request.
|
||||
</div>
|
||||
)}
|
||||
|
||||
{parameters.map((param, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="border rounded-lg p-4 space-y-3 bg-muted/20"
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm font-medium text-muted-foreground">
|
||||
Preset Parameter {index + 1}
|
||||
</span>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => removeParameter(index)}
|
||||
disabled={disabled}
|
||||
className="h-8 w-8"
|
||||
>
|
||||
<Trash2Icon className="h-4 w-4 text-muted-foreground hover:text-destructive" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-xs">Name</Label>
|
||||
<Label className="text-xs text-muted-foreground">
|
||||
Key sent to the API, like "phone_number" or "customer_id"
|
||||
</Label>
|
||||
<Input
|
||||
placeholder="e.g., phone_number"
|
||||
value={param.name}
|
||||
onChange={(e) =>
|
||||
updateParameter(index, "name", e.target.value)
|
||||
}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-xs">Type</Label>
|
||||
<Label className="text-xs text-muted-foreground">
|
||||
JSON type to send to the API
|
||||
</Label>
|
||||
<Select
|
||||
value={param.type}
|
||||
onValueChange={(value: ParameterType) =>
|
||||
updateParameter(index, "type", value)
|
||||
}
|
||||
disabled={disabled}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select type" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="string">String</SelectItem>
|
||||
<SelectItem value="number">Number</SelectItem>
|
||||
<SelectItem value="boolean">Boolean</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-xs">Value or Template</Label>
|
||||
<Label className="text-xs text-muted-foreground">
|
||||
Use a fixed value or a template like {`{{initial_context.phone_number}}`} or {`{{gathered_context.customer_id}}`}
|
||||
</Label>
|
||||
<Input
|
||||
placeholder="e.g., {{initial_context.phone_number}}"
|
||||
value={param.valueTemplate}
|
||||
onChange={(e) =>
|
||||
updateParameter(index, "valueTemplate", e.target.value)
|
||||
}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
id={`preset-required-${index}`}
|
||||
checked={param.required}
|
||||
onCheckedChange={(checked) =>
|
||||
updateParameter(index, "required", checked)
|
||||
}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<Label htmlFor={`preset-required-${index}`} className="text-sm">
|
||||
Required
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={addParameter}
|
||||
className="w-fit"
|
||||
disabled={disabled}
|
||||
>
|
||||
<PlusIcon className="h-4 w-4 mr-1" /> Add Preset Parameter
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue