Implement cost calculator for Tuber (#471)

* Adding cost calculation in tuner for BYOK

* fix

* implement cost calculator for Tuner

* Update api/services/integrations/tuner/completion.py

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

* feat: expose render_options in node spec

* Update api/services/integrations/registry.py

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: mohamed salem <259547077+mohamedsalem-bot@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: Abhishek Kumar <abhishek@a6k.me>
This commit is contained in:
Mohamed-Mamdouh 2026-07-02 08:21:14 +01:00 committed by GitHub
parent 97803b8121
commit 65d46bc313
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 479 additions and 188 deletions

File diff suppressed because one or more lines are too long

View file

@ -3711,6 +3711,20 @@ export type NodeTypesResponse = {
node_types: Array<NodeSpec>;
};
/**
* NumberInputOptions
*
* Renderer hints for numeric inputs.
*/
export type NumberInputOptions = {
/**
* Fractional
*
* Allow arbitrary fractional values via step='any'.
*/
fractional?: boolean;
};
/**
* OnboardingState
*
@ -4353,6 +4367,20 @@ export type ProcessDocumentRequestSchema = {
retrieval_mode?: string;
};
/**
* PropertyLayoutOptions
*
* Renderer layout hints for a property in the node editor.
*/
export type PropertyLayoutOptions = {
/**
* Column Span
*
* Number of columns to occupy in the editor's 12-column grid.
*/
column_span?: number | null;
};
/**
* PropertyOption
*
@ -4373,6 +4401,18 @@ export type PropertyOption = {
description?: string | null;
};
/**
* PropertyRendererOptions
*
* Typed renderer metadata for node properties.
*
* Add new renderer behavior here instead of using free-form property metadata.
*/
export type PropertyRendererOptions = {
layout?: PropertyLayoutOptions | null;
number_input?: NumberInputOptions | null;
};
/**
* PropertySpec
*
@ -4454,12 +4494,7 @@ export type PropertySpec = {
* Editor
*/
editor?: string | null;
/**
* Extra
*/
extra?: {
[key: string]: unknown;
};
renderer_options?: PropertyRendererOptions | null;
};
/**

View file

@ -4,6 +4,7 @@ import type { NodeSpec } from "@/client/types.gen";
import { evaluateDisplayOptions } from "./displayOptions";
import { PropertyInput, type RendererContext } from "./PropertyInput";
import { getPropertyColumnSpan } from "./propertyRendererOptions";
export interface NodeEditFormProps {
spec: NodeSpec;
@ -13,6 +14,21 @@ export interface NodeEditFormProps {
context: RendererContext;
}
const COLUMN_SPAN_CLASS: Record<number, string> = {
1: "sm:col-span-1",
2: "sm:col-span-2",
3: "sm:col-span-3",
4: "sm:col-span-4",
5: "sm:col-span-5",
6: "sm:col-span-6",
7: "sm:col-span-7",
8: "sm:col-span-8",
9: "sm:col-span-9",
10: "sm:col-span-10",
11: "sm:col-span-11",
12: "sm:col-span-12",
};
/**
* Generic node-edit form. Walks `spec.properties` once, evaluates each
* property's `display_options` against current values, and renders the
@ -31,18 +47,25 @@ export function NodeEditForm({ spec, values, onChange, context }: NodeEditFormPr
);
return (
<div className="grid gap-3">
<div className="grid grid-cols-12 gap-3">
{spec.properties
.filter((p) => evaluateDisplayOptions(p.display_options, values))
.map((p) => (
<PropertyInput
key={p.name}
spec={p}
value={values[p.name]}
onChange={(v) => setProp(p.name, v)}
context={context}
/>
))}
.map((p) => {
const columnSpan = getPropertyColumnSpan(p.renderer_options);
return (
<div
key={p.name}
className={`col-span-12 ${COLUMN_SPAN_CLASS[columnSpan]}`}
>
<PropertyInput
spec={p}
value={values[p.name]}
onChange={(v) => setProp(p.name, v)}
context={context}
/>
</div>
);
})}
</div>
);
}

View file

@ -18,6 +18,10 @@ import { Switch } from "@/components/ui/switch";
import { Textarea } from "@/components/ui/textarea";
import { evaluateDisplayOptions } from "./displayOptions";
import {
getPropertyColumnSpan,
isFractionalNumberInput,
} from "./propertyRendererOptions";
export interface RendererContext {
tools: ToolResponse[];
@ -175,13 +179,21 @@ function StringWidget({ spec, value, onChange }: WidgetProps) {
function NumberWidget({ spec, value, onChange }: WidgetProps) {
const v = (value as number | undefined) ?? "";
const isCompact = getPropertyColumnSpan(spec.renderer_options) < 12;
const isFractional = isFractionalNumberInput(spec.renderer_options);
return (
<div className="grid gap-2">
<StackedLabel spec={spec} />
<Input
type="number"
value={v as number | string}
step={spec.min_value && spec.min_value < 1 ? 0.1 : 1}
step={
isFractional
? "any"
: spec.min_value && spec.min_value < 1
? 0.1
: 1
}
min={spec.min_value ?? undefined}
max={spec.max_value ?? undefined}
onChange={(e) => {
@ -189,7 +201,7 @@ function NumberWidget({ spec, value, onChange }: WidgetProps) {
onChange(next === "" ? undefined : parseFloat(next));
}}
placeholder={spec.placeholder ?? undefined}
className="w-32"
className={isCompact ? "w-full" : "w-32"}
/>
</div>
);

View file

@ -0,0 +1,17 @@
import type { PropertyRendererOptions } from "@/client/types.gen";
export function getPropertyColumnSpan(
rendererOptions: PropertyRendererOptions | null | undefined,
): number {
const value = rendererOptions?.layout?.column_span;
if (typeof value !== "number" || !Number.isFinite(value)) {
return 12;
}
return Math.min(Math.max(Math.trunc(value), 1), 12);
}
export function isFractionalNumberInput(
rendererOptions: PropertyRendererOptions | null | undefined,
): boolean {
return rendererOptions?.number_input?.fractional === true;
}