Skip to content
Closed
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
6 changes: 6 additions & 0 deletions packages/core/src/api/api/resources/unstable/client/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/

import * as core from "../../../../core/index.js";
import { DashboardWidgets } from "../resources/dashboardWidgets/client/Client.js";
import { EvaluationRules } from "../resources/evaluationRules/client/Client.js";
import { Evaluators } from "../resources/evaluators/client/Client.js";

Expand All @@ -29,13 +30,18 @@ export declare namespace Unstable {

export class Unstable {
protected readonly _options: Unstable.Options;
protected _dashboardWidgets: DashboardWidgets | undefined;
protected _evaluationRules: EvaluationRules | undefined;
protected _evaluators: Evaluators | undefined;

constructor(_options: Unstable.Options) {
this._options = _options;
}

public get dashboardWidgets(): DashboardWidgets {
return (this._dashboardWidgets ??= new DashboardWidgets(this._options));
}

public get evaluationRules(): EvaluationRules {
return (this._evaluationRules ??= new EvaluationRules(this._options));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
/**
* This file was auto-generated by Fern from our API Definition.
*/

import * as core from "../../../../../../core/index.js";
import * as LangfuseAPI from "../../../../../index.js";
import {
mergeHeaders,
mergeOnlyDefinedHeaders,
} from "../../../../../../core/headers.js";
import * as errors from "../../../../../../errors/index.js";

export declare namespace DashboardWidgets {
export interface Options {
environment: core.Supplier<string>;
/** Specify a custom URL to connect the client to. */
baseUrl?: core.Supplier<string>;
username?: core.Supplier<string | undefined>;
password?: core.Supplier<string | undefined>;
/** Override the X-Langfuse-Sdk-Name header */
xLangfuseSdkName?: core.Supplier<string | undefined>;
/** Override the X-Langfuse-Sdk-Version header */
xLangfuseSdkVersion?: core.Supplier<string | undefined>;
/** Override the X-Langfuse-Public-Key header */
xLangfusePublicKey?: core.Supplier<string | undefined>;
/** Additional headers to include in requests. */
headers?: Record<
string,
string | core.Supplier<string | null | undefined> | null | undefined
>;
}

export interface RequestOptions {
/** The maximum time to wait for a response in seconds. */
timeoutInSeconds?: number;
/** The number of times to retry the request. Defaults to 2. */
maxRetries?: number;
/** A hook to abort the request. */
abortSignal?: AbortSignal;
/** Override the X-Langfuse-Sdk-Name header */
xLangfuseSdkName?: string | undefined;
/** Override the X-Langfuse-Sdk-Version header */
xLangfuseSdkVersion?: string | undefined;
/** Override the X-Langfuse-Public-Key header */
xLangfusePublicKey?: string | undefined;
/** Additional query string parameters to include in the request. */
queryParams?: Record<string, unknown>;
/** Additional headers to include in the request. */
headers?: Record<
string,
string | core.Supplier<string | null | undefined> | null | undefined
>;
}
}

export class DashboardWidgets {
protected readonly _options: DashboardWidgets.Options;

constructor(_options: DashboardWidgets.Options) {
this._options = _options;
}

/**
* Create a reusable dashboard widget.
*
* This endpoint creates the widget. It does not place the widget on a dashboard grid, this has to be done in the UI.
*
* Supported views are `observations`, `scores-numeric`, and `scores-categorical`.
* The legacy `traces` view is not supported by this unstable API, `minVersion` defaults to `2`; values below `2` are rejected.
*
* Unstable API note:
* - This surface may evolve while dashboard/widget APIs are being finalized.
*
* @param {LangfuseAPI.unstable.CreateDashboardWidgetRequest} request
* @param {DashboardWidgets.RequestOptions} requestOptions - Request-specific configuration.
*
* @throws {@link LangfuseAPI.unstable.BadRequestError}
* @throws {@link LangfuseAPI.unstable.UnauthorizedError}
* @throws {@link LangfuseAPI.unstable.AccessDeniedError}
* @throws {@link LangfuseAPI.unstable.MethodNotAllowedError}
* @throws {@link LangfuseAPI.unstable.TooManyRequestsError}
* @throws {@link LangfuseAPI.unstable.InternalServerError}
* @throws {@link LangfuseAPI.Error}
* @throws {@link LangfuseAPI.UnauthorizedError}
* @throws {@link LangfuseAPI.AccessDeniedError}
* @throws {@link LangfuseAPI.MethodNotAllowedError}
* @throws {@link LangfuseAPI.NotFoundError}
*
* @example
* await client.unstable.dashboardWidgets.create({
* name: "Total cost by model",
* description: "Total model cost grouped by provided model name.",
* view: "observations",
* dimensions: [{
* field: "providedModelName"
* }],
* metrics: [{
* measure: "totalCost",
* agg: "sum"
* }],
* filters: [],
* chartType: "HORIZONTAL_BAR",
* chartConfig: {
* type: "HORIZONTAL_BAR",
* row_limit: 10
* },
* minVersion: 2
* })
*/
public create(
request: LangfuseAPI.unstable.CreateDashboardWidgetRequest,
requestOptions?: DashboardWidgets.RequestOptions,
): core.HttpResponsePromise<LangfuseAPI.unstable.DashboardWidget> {
return core.HttpResponsePromise.fromPromise(
this.__create(request, requestOptions),
);
}

private async __create(
request: LangfuseAPI.unstable.CreateDashboardWidgetRequest,
requestOptions?: DashboardWidgets.RequestOptions,
): Promise<core.WithRawResponse<LangfuseAPI.unstable.DashboardWidget>> {
let _headers: core.Fetcher.Args["headers"] = mergeHeaders(
this._options?.headers,
mergeOnlyDefinedHeaders({
Authorization: await this._getAuthorizationHeader(),
"X-Langfuse-Sdk-Name":
requestOptions?.xLangfuseSdkName ?? this._options?.xLangfuseSdkName,
"X-Langfuse-Sdk-Version":
requestOptions?.xLangfuseSdkVersion ??
this._options?.xLangfuseSdkVersion,
"X-Langfuse-Public-Key":
requestOptions?.xLangfusePublicKey ??
this._options?.xLangfusePublicKey,
}),
requestOptions?.headers,
);
const _response = await core.fetcher({
url: core.url.join(
(await core.Supplier.get(this._options.baseUrl)) ??
(await core.Supplier.get(this._options.environment)),
"/api/public/unstable/dashboard-widgets",
),
method: "POST",
headers: _headers,
contentType: "application/json",
queryParameters: requestOptions?.queryParams,
requestType: "json",
body: request,
timeoutMs:
requestOptions?.timeoutInSeconds != null
? requestOptions.timeoutInSeconds * 1000
: 60000,
maxRetries: requestOptions?.maxRetries,
abortSignal: requestOptions?.abortSignal,
});
if (_response.ok) {
return {
data: _response.body as LangfuseAPI.unstable.DashboardWidget,
rawResponse: _response.rawResponse,
};
}

if (_response.error.reason === "status-code") {
switch (_response.error.statusCode) {
case 400:
throw new LangfuseAPI.unstable.BadRequestError(
_response.error.body as LangfuseAPI.unstable.PublicApiError,
_response.rawResponse,
);
case 401:
throw new LangfuseAPI.unstable.UnauthorizedError(
_response.error.body as LangfuseAPI.unstable.PublicApiError,
_response.rawResponse,
);
case 403:
throw new LangfuseAPI.unstable.AccessDeniedError(
_response.error.body as LangfuseAPI.unstable.PublicApiError,
_response.rawResponse,
);
case 405:
throw new LangfuseAPI.unstable.MethodNotAllowedError(
_response.error.body as LangfuseAPI.unstable.PublicApiError,
_response.rawResponse,
);
case 429:
throw new LangfuseAPI.unstable.TooManyRequestsError(
_response.error.body as LangfuseAPI.unstable.PublicApiError,
_response.rawResponse,
);
case 500:
throw new LangfuseAPI.unstable.InternalServerError(
_response.error.body as LangfuseAPI.unstable.PublicApiError,
_response.rawResponse,
);
case 400:
throw new LangfuseAPI.Error(
_response.error.body as unknown,
_response.rawResponse,
);
case 401:
throw new LangfuseAPI.UnauthorizedError(
_response.error.body as unknown,
_response.rawResponse,
);
case 403:
throw new LangfuseAPI.AccessDeniedError(
_response.error.body as unknown,
_response.rawResponse,
);
case 405:
throw new LangfuseAPI.MethodNotAllowedError(
_response.error.body as unknown,
_response.rawResponse,
);
Comment on lines +196 to +215

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Dead code: duplicate case labels shadow second error block

The switch on _response.error.statusCode declares case 400, 401, 403, and 405 twice. In JavaScript/TypeScript a switch executes the first matching arm and exits (each arm here ends with throw), so the second set of cases — throwing LangfuseAPI.Error, LangfuseAPI.UnauthorizedError, LangfuseAPI.AccessDeniedError, and LangfuseAPI.MethodNotAllowedError — can never be reached. Code that catches those specific error classes will never fire on these status codes.

Note: case 404 (line 216) is unique and is reachable since 404 appears only in the second block. The same duplicate-case pattern appears in the pre-existing evaluationRules/client/Client.ts and likely originates from the Fern generator — worth a fix upstream in the API spec or generator config.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/core/src/api/api/resources/unstable/resources/dashboardWidgets/client/Client.ts
Line: 196-215

Comment:
**Dead code: duplicate `case` labels shadow second error block**

The `switch` on `_response.error.statusCode` declares `case 400`, `401`, `403`, and `405` twice. In JavaScript/TypeScript a `switch` executes the first matching arm and exits (each arm here ends with `throw`), so the second set of cases — throwing `LangfuseAPI.Error`, `LangfuseAPI.UnauthorizedError`, `LangfuseAPI.AccessDeniedError`, and `LangfuseAPI.MethodNotAllowedError` — can never be reached. Code that `catch`es those specific error classes will never fire on these status codes.

Note: `case 404` (line 216) is unique and *is* reachable since 404 appears only in the second block. The same duplicate-case pattern appears in the pre-existing `evaluationRules/client/Client.ts` and likely originates from the Fern generator — worth a fix upstream in the API spec or generator config.

How can I resolve this? If you propose a fix, please make it concise.

case 404:
throw new LangfuseAPI.NotFoundError(
_response.error.body as unknown,
_response.rawResponse,
);
default:
throw new errors.LangfuseAPIError({
statusCode: _response.error.statusCode,
body: _response.error.body,
rawResponse: _response.rawResponse,
});
}
}

switch (_response.error.reason) {
case "non-json":
throw new errors.LangfuseAPIError({
statusCode: _response.error.statusCode,
body: _response.error.rawBody,
rawResponse: _response.rawResponse,
});
case "timeout":
throw new errors.LangfuseAPITimeoutError(
"Timeout exceeded when calling POST /api/public/unstable/dashboard-widgets.",
);
case "unknown":
throw new errors.LangfuseAPIError({
message: _response.error.errorMessage,
rawResponse: _response.rawResponse,
});
}
}

protected async _getAuthorizationHeader(): Promise<string | undefined> {
const username = await core.Supplier.get(this._options.username);
const password = await core.Supplier.get(this._options.password);
if (username != null && password != null) {
return core.BasicAuth.toAuthorizationHeader({
username: username,
password: password,
});
}

return undefined;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./types/index.js";
export * from "./client/index.js";
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* This file was auto-generated by Fern from our API Definition.
*/

import * as LangfuseAPI from "../../../../../index.js";

export interface CreateDashboardWidgetRequest {
name: string;
description: string;
view: LangfuseAPI.unstable.DashboardWidgetView;
dimensions: LangfuseAPI.unstable.DashboardWidgetDimension[];
metrics: LangfuseAPI.unstable.DashboardWidgetMetric[];
filters: LangfuseAPI.unstable.DashboardWidgetFilter[];
chartType: LangfuseAPI.unstable.DashboardWidgetChartType;
chartConfig: LangfuseAPI.unstable.DashboardWidgetChartConfig;
minVersion?: number;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* This file was auto-generated by Fern from our API Definition.
*/

import * as LangfuseAPI from "../../../../../index.js";

export interface DashboardWidget {
id: string;
createdAt: string;
updatedAt: string;
name: string;
description: string;
view: LangfuseAPI.unstable.DashboardWidgetView;
dimensions: LangfuseAPI.unstable.DashboardWidgetDimension[];
metrics: LangfuseAPI.unstable.DashboardWidgetMetric[];
filters: LangfuseAPI.unstable.DashboardWidgetFilter[];
chartType: LangfuseAPI.unstable.DashboardWidgetChartType;
chartConfig: LangfuseAPI.unstable.DashboardWidgetChartConfig;
minVersion: number;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* This file was auto-generated by Fern from our API Definition.
*/

import * as LangfuseAPI from "../../../../../index.js";

/**
* Chart-specific widget configuration.
*
* `type` must match the top-level `chartType`.
* `row_limit` applies to total-value charts and pivot tables.
* `bins` applies to histograms.
* `defaultSort` applies to pivot tables.
*/
export interface DashboardWidgetChartConfig {
type: LangfuseAPI.unstable.DashboardWidgetChartType;
row_limit?: number;
show_value_labels?: boolean;
bins?: number;
defaultSort?: LangfuseAPI.unstable.DashboardWidgetDefaultSort;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* This file was auto-generated by Fern from our API Definition.
*/

export type DashboardWidgetChartType =
| "LINE_TIME_SERIES"
| "AREA_TIME_SERIES"
| "BAR_TIME_SERIES"
| "HORIZONTAL_BAR"
| "VERTICAL_BAR"
| "PIE"
| "NUMBER"
| "HISTOGRAM"
| "PIVOT_TABLE";
export const DashboardWidgetChartType = {
LineTimeSeries: "LINE_TIME_SERIES",
AreaTimeSeries: "AREA_TIME_SERIES",
BarTimeSeries: "BAR_TIME_SERIES",
HorizontalBar: "HORIZONTAL_BAR",
VerticalBar: "VERTICAL_BAR",
Pie: "PIE",
Number: "NUMBER",
Histogram: "HISTOGRAM",
PivotTable: "PIVOT_TABLE",
} as const;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* This file was auto-generated by Fern from our API Definition.
*/

import * as LangfuseAPI from "../../../../../index.js";

export interface DashboardWidgetDefaultSort {
column: string;
order: LangfuseAPI.unstable.DashboardWidgetSortOrder;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* This file was auto-generated by Fern from our API Definition.
*/

export interface DashboardWidgetDimension {
field: string;
}
Loading