mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
feat: Add LLM (Large Language Model) settings and management features
- Introduced new settings page for managing global LLM configurations. - Added routes and permissions for LLM management in the admin dashboard. - Implemented LLM model with necessary fields and access controls. - Created API endpoints for fetching global LLMs. - Developed UI components for displaying and editing LLM details. - Integrated LLM settings into the existing admin dashboard structure. - Added support for multiple LLM providers including OpenAI, Anthropic, and Ollama.
This commit is contained in:
@@ -9,6 +9,7 @@ import SettingsWhatsApp from "./Pages/Settings/WhatsApp/Index";
|
||||
// Settings Pages.
|
||||
import SettingsEmail from "./Pages/Settings/Email/Index";
|
||||
import SettingsProbes from "./Pages/Settings/Probes/Index";
|
||||
import SettingsLlms from "./Pages/Settings/Llms/Index";
|
||||
import Users from "./Pages/Users/Index";
|
||||
import PageMap from "./Utils/PageMap";
|
||||
import RouteMap from "./Utils/RouteMap";
|
||||
@@ -122,6 +123,11 @@ const App: () => JSX.Element = () => {
|
||||
element={<SettingsProbes />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.SETTINGS_LLMS]?.toString() || ""}
|
||||
element={<SettingsLlms />}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.SETTINGS_AUTHENTICATION]?.toString() || ""}
|
||||
element={<SettingsAuthentication />}
|
||||
|
||||
238
AdminDashboard/src/Pages/Settings/Llms/Index.tsx
Normal file
238
AdminDashboard/src/Pages/Settings/Llms/Index.tsx
Normal file
@@ -0,0 +1,238 @@
|
||||
import AdminModelAPI from "../../../Utils/ModelAPI";
|
||||
import PageMap from "../../../Utils/PageMap";
|
||||
import RouteMap, { RouteUtil } from "../../../Utils/RouteMap";
|
||||
import DashboardSideMenu from "../SideMenu";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import IsNull from "Common/Types/BaseDatabase/IsNull";
|
||||
import Banner from "Common/UI/Components/Banner/Banner";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import ModelTable from "Common/UI/Components/ModelTable/ModelTable";
|
||||
import Page from "Common/UI/Components/Page/Page";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import Llm from "Common/Models/DatabaseModels/Llm";
|
||||
import LlmType from "Common/Types/LLM/LlmType";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
import Pill from "Common/UI/Components/Pill/Pill";
|
||||
import { Green, Red } from "Common/Types/BrandColors";
|
||||
import DropdownUtil from "Common/UI/Utils/Dropdown";
|
||||
|
||||
const Settings: FunctionComponent = (): ReactElement => {
|
||||
return (
|
||||
<Page
|
||||
title={"Admin Settings"}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: "Admin Dashboard",
|
||||
to: RouteUtil.populateRouteParams(RouteMap[PageMap.HOME] as Route),
|
||||
},
|
||||
{
|
||||
title: "Settings",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS] as Route,
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Global LLMs",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_LLMS] as Route,
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<DashboardSideMenu />}
|
||||
>
|
||||
{/* LLM Settings View */}
|
||||
|
||||
<Banner
|
||||
openInNewTab={true}
|
||||
title="Need help with setting up LLMs?"
|
||||
description="LLMs (Large Language Models) enable AI features. You can configure global LLMs that are available to all projects."
|
||||
link={Route.fromString("/docs/ai/llm")}
|
||||
hideOnMobile={true}
|
||||
/>
|
||||
|
||||
<ModelTable<Llm>
|
||||
userPreferencesKey={"admin-llms-table"}
|
||||
modelType={Llm}
|
||||
id="llms-table"
|
||||
name="Settings > Global LLMs"
|
||||
isDeleteable={true}
|
||||
isEditable={true}
|
||||
isCreateable={true}
|
||||
cardProps={{
|
||||
title: "Global LLMs",
|
||||
description:
|
||||
"Global LLMs are available to all projects for AI features. Configure OpenAI, Anthropic, Ollama, or other LLM providers.",
|
||||
}}
|
||||
query={{
|
||||
projectId: new IsNull(),
|
||||
isGlobalLlm: true,
|
||||
}}
|
||||
modelAPI={AdminModelAPI}
|
||||
noItemsMessage={"No LLMs configured. Add an LLM to enable AI features."}
|
||||
showRefreshButton={true}
|
||||
onBeforeCreate={(item: Llm) => {
|
||||
item.isGlobalLlm = true;
|
||||
return Promise.resolve(item);
|
||||
}}
|
||||
formSteps={[
|
||||
{
|
||||
title: "Basic Info",
|
||||
id: "basic-info",
|
||||
},
|
||||
{
|
||||
title: "Provider Settings",
|
||||
id: "provider-settings",
|
||||
},
|
||||
]}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: "Name",
|
||||
stepId: "basic-info",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
placeholder: "My OpenAI GPT-4",
|
||||
validation: {
|
||||
minLength: 2,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
field: {
|
||||
description: true,
|
||||
},
|
||||
title: "Description",
|
||||
stepId: "basic-info",
|
||||
fieldType: FormFieldSchemaType.LongText,
|
||||
required: false,
|
||||
placeholder: "GPT-4 for general AI features.",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
llmType: true,
|
||||
},
|
||||
title: "LLM Provider",
|
||||
stepId: "provider-settings",
|
||||
fieldType: FormFieldSchemaType.Dropdown,
|
||||
required: true,
|
||||
placeholder: "Select LLM Provider",
|
||||
dropdownOptions: DropdownUtil.getDropdownOptionsFromEnum(LlmType),
|
||||
},
|
||||
{
|
||||
field: {
|
||||
apiKey: true,
|
||||
},
|
||||
title: "API Key",
|
||||
stepId: "provider-settings",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: false,
|
||||
placeholder: "sk-...",
|
||||
description:
|
||||
"Required for OpenAI and Anthropic. Not required for Ollama if self-hosted.",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
modelName: true,
|
||||
},
|
||||
title: "Model Name",
|
||||
stepId: "provider-settings",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: false,
|
||||
placeholder: "gpt-4, claude-3-opus, llama2",
|
||||
description:
|
||||
"The specific model to use (e.g., gpt-4, claude-3-opus, llama2).",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
baseUrl: true,
|
||||
},
|
||||
title: "Base URL",
|
||||
stepId: "provider-settings",
|
||||
fieldType: FormFieldSchemaType.URL,
|
||||
required: false,
|
||||
placeholder: "http://localhost:11434",
|
||||
description:
|
||||
"Required for Ollama. Optional for others to use custom endpoints.",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isEnabled: true,
|
||||
},
|
||||
title: "Enabled",
|
||||
stepId: "provider-settings",
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
required: false,
|
||||
description: "Enable or disable this LLM configuration.",
|
||||
},
|
||||
]}
|
||||
selectMoreFields={{
|
||||
apiKey: true,
|
||||
}}
|
||||
filters={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: "Name",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
description: true,
|
||||
},
|
||||
title: "Description",
|
||||
type: FieldType.LongText,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
llmType: true,
|
||||
},
|
||||
title: "Provider",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
]}
|
||||
columns={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: "Name",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
llmType: true,
|
||||
},
|
||||
title: "Provider",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
modelName: true,
|
||||
},
|
||||
title: "Model",
|
||||
type: FieldType.Text,
|
||||
noValueMessage: "-",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isEnabled: true,
|
||||
},
|
||||
title: "Status",
|
||||
type: FieldType.Boolean,
|
||||
getElement: (item: Llm): ReactElement => {
|
||||
if (item.isEnabled) {
|
||||
return <Pill text="Enabled" color={Green} />;
|
||||
}
|
||||
return <Pill text="Disabled" color={Red} />;
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default Settings;
|
||||
@@ -72,6 +72,17 @@ const DashboardSideMenu: () => JSX.Element = (): ReactElement => {
|
||||
icon={IconProp.Signal}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
<SideMenuSection title="AI">
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: "Global LLMs",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_LLMS] as Route,
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Robot}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
<SideMenuSection title="API and Integrations">
|
||||
<SideMenuItem
|
||||
link={{
|
||||
|
||||
@@ -18,6 +18,7 @@ enum PageMap {
|
||||
SETTINGS_CALL_AND_SMS = "SETTINGS_CALL_AND_SMS",
|
||||
SETTINGS_WHATSAPP = "SETTINGS_WHATSAPP",
|
||||
SETTINGS_PROBES = "SETTINGS_PROBES",
|
||||
SETTINGS_LLMS = "SETTINGS_LLMS",
|
||||
SETTINGS_AUTHENTICATION = "SETTINGS_AUTHENTICATION",
|
||||
SETTINGS_API_KEY = "SETTINGS_API_KEY",
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ const RouteMap: Dictionary<Route> = {
|
||||
[PageMap.SETTINGS_CALL_AND_SMS]: new Route(`/admin/settings/call-and-sms`),
|
||||
[PageMap.SETTINGS_WHATSAPP]: new Route(`/admin/settings/whatsapp`),
|
||||
[PageMap.SETTINGS_PROBES]: new Route(`/admin/settings/probes`),
|
||||
[PageMap.SETTINGS_LLMS]: new Route(`/admin/settings/llms`),
|
||||
[PageMap.SETTINGS_AUTHENTICATION]: new Route(
|
||||
`/admin/settings/authentication`,
|
||||
),
|
||||
|
||||
@@ -12,6 +12,7 @@ import MonitorGroupAPI from "Common/Server/API/MonitorGroupAPI";
|
||||
import NotificationAPI from "Common/Server/API/NotificationAPI";
|
||||
import TelemetryAPI from "Common/Server/API/TelemetryAPI";
|
||||
import ProbeAPI from "Common/Server/API/ProbeAPI";
|
||||
import LlmAPI from "Common/Server/API/LlmAPI";
|
||||
import ProjectAPI from "Common/Server/API/ProjectAPI";
|
||||
import ProjectSsoAPI from "Common/Server/API/ProjectSSO";
|
||||
import WhatsAppLogAPI from "./WhatsAppLogAPI";
|
||||
@@ -1702,6 +1703,7 @@ const BaseAPIFeatureSet: FeatureSet = {
|
||||
);
|
||||
app.use(`/${APP_NAME.toLocaleLowerCase()}`, new UserPushAPI().getRouter());
|
||||
app.use(`/${APP_NAME.toLocaleLowerCase()}`, new ProbeAPI().getRouter());
|
||||
app.use(`/${APP_NAME.toLocaleLowerCase()}`, new LlmAPI().getRouter());
|
||||
|
||||
app.use(
|
||||
`/${APP_NAME.toLocaleLowerCase()}`,
|
||||
|
||||
@@ -74,6 +74,7 @@ import OnCallDutyPolicyTimeLog from "./OnCallDutyPolicyTimeLog";
|
||||
import Probe from "./Probe";
|
||||
import ProbeOwnerTeam from "./ProbeOwnerTeam";
|
||||
import ProbeOwnerUser from "./ProbeOwnerUser";
|
||||
import Llm from "./Llm";
|
||||
import Project from "./Project";
|
||||
import ProjectCallSMSConfig from "./ProjectCallSMSConfig";
|
||||
// Project SMTP Config.
|
||||
@@ -381,6 +382,8 @@ const AllModelTypes: Array<{
|
||||
|
||||
ProbeOwnerTeam,
|
||||
ProbeOwnerUser,
|
||||
|
||||
Llm,
|
||||
|
||||
UserSession,
|
||||
UserTotpAuth,
|
||||
|
||||
464
Common/Models/DatabaseModels/Llm.ts
Normal file
464
Common/Models/DatabaseModels/Llm.ts
Normal file
@@ -0,0 +1,464 @@
|
||||
import Project from "./Project";
|
||||
import User from "./User";
|
||||
import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
|
||||
import Route from "../../Types/API/Route";
|
||||
import { PlanType } from "../../Types/Billing/SubscriptionPlan";
|
||||
import ColumnAccessControl from "../../Types/Database/AccessControl/ColumnAccessControl";
|
||||
import TableAccessControl from "../../Types/Database/AccessControl/TableAccessControl";
|
||||
import TableBillingAccessControl from "../../Types/Database/AccessControl/TableBillingAccessControl";
|
||||
import ColumnLength from "../../Types/Database/ColumnLength";
|
||||
import ColumnType from "../../Types/Database/ColumnType";
|
||||
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
|
||||
import IsPermissionsIf from "../../Types/Database/IsPermissionsIf";
|
||||
import SlugifyColumn from "../../Types/Database/SlugifyColumn";
|
||||
import TableColumn from "../../Types/Database/TableColumn";
|
||||
import TableColumnType from "../../Types/Database/TableColumnType";
|
||||
import TableMetadata from "../../Types/Database/TableMetadata";
|
||||
import TenantColumn from "../../Types/Database/TenantColumn";
|
||||
import IconProp from "../../Types/Icon/IconProp";
|
||||
import ObjectID from "../../Types/ObjectID";
|
||||
import Permission from "../../Types/Permission";
|
||||
import { Column, Entity, JoinColumn, ManyToOne } from "typeorm";
|
||||
import EnableDocumentation from "../../Types/Database/EnableDocumentation";
|
||||
import LlmType from "../../Types/LLM/LlmType";
|
||||
|
||||
@EnableDocumentation()
|
||||
@TableBillingAccessControl({
|
||||
create: PlanType.Growth,
|
||||
read: PlanType.Free,
|
||||
update: PlanType.Growth,
|
||||
delete: PlanType.Free,
|
||||
})
|
||||
@IsPermissionsIf(Permission.Public, "projectId", null)
|
||||
@TenantColumn("projectId")
|
||||
@CrudApiEndpoint(new Route("/llm"))
|
||||
@SlugifyColumn("name", "slug")
|
||||
@Entity({
|
||||
name: "LLM",
|
||||
})
|
||||
@TableMetadata({
|
||||
tableName: "LLM",
|
||||
singularName: "LLM",
|
||||
pluralName: "LLMs",
|
||||
icon: IconProp.Bolt,
|
||||
tableDescription:
|
||||
"Manage LLM (Large Language Model) configurations. Connect to OpenAI, Anthropic, Ollama, or other LLM providers to enable AI features.",
|
||||
})
|
||||
@TableAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectLlm,
|
||||
],
|
||||
read: [
|
||||
Permission.Public,
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.ReadProjectLlm,
|
||||
],
|
||||
delete: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.DeleteProjectLlm,
|
||||
],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditProjectLlm,
|
||||
],
|
||||
})
|
||||
export default class LLM extends BaseModel {
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectLlm,
|
||||
],
|
||||
read: [Permission.Public],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditProjectLlm,
|
||||
],
|
||||
})
|
||||
@TableColumn({
|
||||
required: true,
|
||||
type: TableColumnType.Name,
|
||||
canReadOnRelationQuery: true,
|
||||
title: "Name",
|
||||
description: "A friendly name for this LLM configuration.",
|
||||
})
|
||||
@Column({
|
||||
nullable: false,
|
||||
type: ColumnType.Name,
|
||||
length: ColumnLength.Name,
|
||||
})
|
||||
public name?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectLlm,
|
||||
],
|
||||
read: [Permission.Public],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditProjectLlm,
|
||||
],
|
||||
})
|
||||
@TableColumn({
|
||||
required: false,
|
||||
type: TableColumnType.LongText,
|
||||
title: "Description",
|
||||
description: "Description of this LLM configuration.",
|
||||
})
|
||||
@Column({
|
||||
nullable: true,
|
||||
type: ColumnType.LongText,
|
||||
})
|
||||
public description?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [Permission.Public],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
required: true,
|
||||
unique: true,
|
||||
type: TableColumnType.Slug,
|
||||
computed: true,
|
||||
title: "Slug",
|
||||
description: "Friendly globally unique name for your object",
|
||||
})
|
||||
@Column({
|
||||
nullable: false,
|
||||
type: ColumnType.Slug,
|
||||
length: ColumnLength.Slug,
|
||||
})
|
||||
public slug?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectLlm,
|
||||
],
|
||||
read: [Permission.Public],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditProjectLlm,
|
||||
],
|
||||
})
|
||||
@TableColumn({
|
||||
required: true,
|
||||
type: TableColumnType.ShortText,
|
||||
title: "LLM Type",
|
||||
description: "The type of LLM provider (OpenAI, Anthropic, Ollama, etc.)",
|
||||
})
|
||||
@Column({
|
||||
nullable: false,
|
||||
type: ColumnType.ShortText,
|
||||
length: ColumnLength.ShortText,
|
||||
})
|
||||
public llmType?: LlmType = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectLlm,
|
||||
],
|
||||
read: [Permission.ProjectOwner, Permission.ProjectAdmin],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditProjectLlm,
|
||||
],
|
||||
})
|
||||
@TableColumn({
|
||||
required: false,
|
||||
type: TableColumnType.LongText,
|
||||
title: "API Key",
|
||||
description:
|
||||
"The API key for the LLM provider. Required for OpenAI and Anthropic.",
|
||||
encrypted: true,
|
||||
})
|
||||
@Column({
|
||||
nullable: true,
|
||||
type: ColumnType.LongText,
|
||||
})
|
||||
public apiKey?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectLlm,
|
||||
],
|
||||
read: [Permission.Public],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditProjectLlm,
|
||||
],
|
||||
})
|
||||
@TableColumn({
|
||||
required: false,
|
||||
type: TableColumnType.ShortText,
|
||||
title: "Model Name",
|
||||
description:
|
||||
"The name of the model to use (e.g., gpt-4, claude-3-opus, llama2).",
|
||||
})
|
||||
@Column({
|
||||
nullable: true,
|
||||
type: ColumnType.ShortText,
|
||||
length: ColumnLength.ShortText,
|
||||
})
|
||||
public modelName?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectLlm,
|
||||
],
|
||||
read: [Permission.Public],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditProjectLlm,
|
||||
],
|
||||
})
|
||||
@TableColumn({
|
||||
required: false,
|
||||
type: TableColumnType.ShortURL,
|
||||
title: "Base URL",
|
||||
description:
|
||||
"The base URL for the LLM API. Required for Ollama, optional for others.",
|
||||
})
|
||||
@Column({
|
||||
nullable: true,
|
||||
type: ColumnType.ShortURL,
|
||||
length: ColumnLength.ShortURL,
|
||||
})
|
||||
public baseUrl?: string = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectLlm,
|
||||
],
|
||||
read: [Permission.Public],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
type: TableColumnType.Entity,
|
||||
required: false,
|
||||
modelType: Project,
|
||||
title: "Project",
|
||||
description:
|
||||
"The project this LLM belongs to. If null, it is a global LLM.",
|
||||
})
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return Project;
|
||||
},
|
||||
{
|
||||
cascade: false,
|
||||
eager: false,
|
||||
nullable: true,
|
||||
onDelete: "CASCADE",
|
||||
orphanedRowAction: "nullify",
|
||||
},
|
||||
)
|
||||
@JoinColumn({ name: "projectId" })
|
||||
public project?: Project = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectLlm,
|
||||
],
|
||||
read: [Permission.Public],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
type: TableColumnType.ObjectID,
|
||||
required: false,
|
||||
canReadOnRelationQuery: true,
|
||||
title: "Project ID",
|
||||
description:
|
||||
"ID of the project this LLM belongs to. If null, it is a global LLM.",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.ObjectID,
|
||||
nullable: true,
|
||||
transformer: ObjectID.getDatabaseTransformer(),
|
||||
})
|
||||
public projectId?: ObjectID = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({ type: TableColumnType.Entity, modelType: User })
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return User;
|
||||
},
|
||||
{
|
||||
cascade: false,
|
||||
eager: false,
|
||||
nullable: true,
|
||||
onDelete: "SET NULL",
|
||||
orphanedRowAction: "nullify",
|
||||
},
|
||||
)
|
||||
@JoinColumn({ name: "deletedByUserId" })
|
||||
public deletedByUser?: User = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
type: TableColumnType.ObjectID,
|
||||
title: "Deleted by User ID",
|
||||
description:
|
||||
"User ID who deleted this object (if this object was deleted by a User)",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.ObjectID,
|
||||
nullable: true,
|
||||
transformer: ObjectID.getDatabaseTransformer(),
|
||||
})
|
||||
public deletedByUserId?: ObjectID = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectLlm,
|
||||
],
|
||||
read: [Permission.ProjectOwner],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({ type: TableColumnType.Entity, modelType: User })
|
||||
@ManyToOne(
|
||||
() => {
|
||||
return User;
|
||||
},
|
||||
{
|
||||
eager: false,
|
||||
nullable: true,
|
||||
onDelete: "SET NULL",
|
||||
orphanedRowAction: "nullify",
|
||||
},
|
||||
)
|
||||
@JoinColumn({ name: "createdByUserId" })
|
||||
public createdByUser?: User = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectLlm,
|
||||
],
|
||||
read: [Permission.ProjectOwner],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
type: TableColumnType.ObjectID,
|
||||
title: "Created by User ID",
|
||||
description:
|
||||
"User ID who created this object (if this object was created by a User)",
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.ObjectID,
|
||||
nullable: true,
|
||||
transformer: ObjectID.getDatabaseTransformer(),
|
||||
})
|
||||
public createdByUserId?: ObjectID = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [],
|
||||
read: [],
|
||||
update: [],
|
||||
})
|
||||
@TableColumn({
|
||||
isDefaultValueColumn: true,
|
||||
required: true,
|
||||
type: TableColumnType.Boolean,
|
||||
canReadOnRelationQuery: true,
|
||||
title: "Is Global LLM",
|
||||
description:
|
||||
"Is this a global LLM that is available to all projects? Only admins can create global LLMs.",
|
||||
defaultValue: false,
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.Boolean,
|
||||
nullable: false,
|
||||
unique: false,
|
||||
default: false,
|
||||
})
|
||||
public isGlobalLlm?: boolean = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.CreateProjectLlm,
|
||||
],
|
||||
read: [Permission.Public],
|
||||
update: [
|
||||
Permission.ProjectOwner,
|
||||
Permission.ProjectAdmin,
|
||||
Permission.ProjectMember,
|
||||
Permission.EditProjectLlm,
|
||||
],
|
||||
})
|
||||
@TableColumn({
|
||||
isDefaultValueColumn: true,
|
||||
required: true,
|
||||
type: TableColumnType.Boolean,
|
||||
title: "Is Enabled",
|
||||
description: "Is this LLM configuration enabled and available for use?",
|
||||
defaultValue: true,
|
||||
})
|
||||
@Column({
|
||||
type: ColumnType.Boolean,
|
||||
nullable: false,
|
||||
unique: false,
|
||||
default: true,
|
||||
})
|
||||
public isEnabled?: boolean = undefined;
|
||||
}
|
||||
58
Common/Server/API/LlmAPI.ts
Normal file
58
Common/Server/API/LlmAPI.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import UserMiddleware from "../Middleware/UserAuthorization";
|
||||
import LlmService, {
|
||||
Service as LlmServiceType,
|
||||
} from "../Services/LlmService";
|
||||
import {
|
||||
ExpressRequest,
|
||||
ExpressResponse,
|
||||
NextFunction,
|
||||
} from "../Utils/Express";
|
||||
import Response from "../Utils/Response";
|
||||
import BaseAPI from "./BaseAPI";
|
||||
import LIMIT_MAX from "../../Types/Database/LimitMax";
|
||||
import PositiveNumber from "../../Types/PositiveNumber";
|
||||
import Llm from "../../Models/DatabaseModels/Llm";
|
||||
|
||||
export default class LlmAPI extends BaseAPI<Llm, LlmServiceType> {
|
||||
public constructor() {
|
||||
super(Llm, LlmService);
|
||||
|
||||
this.router.post(
|
||||
`${new this.entityType().getCrudApiPath()?.toString()}/global-llms`,
|
||||
UserMiddleware.getUserMiddleware,
|
||||
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
try {
|
||||
const llms: Array<Llm> = await LlmService.findBy({
|
||||
query: {
|
||||
isGlobalLlm: true,
|
||||
isEnabled: true,
|
||||
},
|
||||
select: {
|
||||
name: true,
|
||||
description: true,
|
||||
llmType: true,
|
||||
modelName: true,
|
||||
baseUrl: true,
|
||||
isEnabled: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
skip: 0,
|
||||
limit: LIMIT_MAX,
|
||||
});
|
||||
|
||||
return Response.sendEntityArrayResponse(
|
||||
req,
|
||||
res,
|
||||
llms,
|
||||
new PositiveNumber(llms.length),
|
||||
Llm,
|
||||
);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import IncidentStateService from "./IncidentStateService";
|
||||
import IncidentStateTimelineService from "./IncidentStateTimelineService";
|
||||
//Labels.
|
||||
import LabelService from "./LabelService";
|
||||
import LlmService from "./LlmService";
|
||||
import LogService from "./LogService";
|
||||
import MailService from "./MailService";
|
||||
import MetricService from "./MetricService";
|
||||
@@ -217,6 +218,7 @@ const services: Array<BaseService> = [
|
||||
IncidentFeedService,
|
||||
|
||||
LabelService,
|
||||
LlmService,
|
||||
|
||||
MailService,
|
||||
MonitorCustomFieldService,
|
||||
|
||||
10
Common/Server/Services/LlmService.ts
Normal file
10
Common/Server/Services/LlmService.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import DatabaseService from "./DatabaseService";
|
||||
import Model from "../../Models/DatabaseModels/Llm";
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor() {
|
||||
super(Model);
|
||||
}
|
||||
}
|
||||
|
||||
export default new Service();
|
||||
4
Common/Types/LLM/Index.ts
Normal file
4
Common/Types/LLM/Index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import LlmType from "./LlmType";
|
||||
|
||||
export default LlmType;
|
||||
export { LlmType };
|
||||
7
Common/Types/LLM/LlmType.ts
Normal file
7
Common/Types/LLM/LlmType.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
enum LlmType {
|
||||
OpenAI = "OpenAI",
|
||||
Anthropic = "Anthropic",
|
||||
Ollama = "Ollama",
|
||||
}
|
||||
|
||||
export default LlmType;
|
||||
@@ -96,6 +96,11 @@ enum Permission {
|
||||
EditProjectProbe = "EditProjectProbe",
|
||||
ReadProjectProbe = "ReadProjectProbe",
|
||||
|
||||
CreateProjectLlm = "CreateProjectLlm",
|
||||
DeleteProjectLlm = "DeleteProjectLlm",
|
||||
EditProjectLlm = "EditProjectLlm",
|
||||
ReadProjectLlm = "ReadProjectLlm",
|
||||
|
||||
CreateTelemetryService = "CreateTelemetryService",
|
||||
DeleteTelemetryService = "DeleteTelemetryService",
|
||||
EditTelemetryService = "EditTelemetryService",
|
||||
@@ -2744,6 +2749,35 @@ export class PermissionHelper {
|
||||
isAccessControlPermission: true,
|
||||
},
|
||||
|
||||
{
|
||||
permission: Permission.CreateProjectLlm,
|
||||
title: "Create LLM",
|
||||
description: "This permission can create LLM configurations for this project.",
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
{
|
||||
permission: Permission.DeleteProjectLlm,
|
||||
title: "Delete LLM",
|
||||
description: "This permission can delete LLM configurations of this project.",
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
{
|
||||
permission: Permission.EditProjectLlm,
|
||||
title: "Edit LLM",
|
||||
description: "This permission can edit LLM configurations of this project.",
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
{
|
||||
permission: Permission.ReadProjectLlm,
|
||||
title: "Read LLM",
|
||||
description: "This permission can read LLM configurations of this project.",
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
|
||||
{
|
||||
permission: Permission.CreateTelemetryService,
|
||||
title: "Create Telemetry Service",
|
||||
|
||||
197
Dashboard/src/Pages/Settings/LlmView.tsx
Normal file
197
Dashboard/src/Pages/Settings/LlmView.tsx
Normal file
@@ -0,0 +1,197 @@
|
||||
import PageMap from "../../Utils/PageMap";
|
||||
import RouteMap from "../../Utils/RouteMap";
|
||||
import PageComponentProps from "../PageComponentProps";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import ModelDelete from "Common/UI/Components/ModelDelete/ModelDelete";
|
||||
import CardModelDetail from "Common/UI/Components/ModelDetail/CardModelDetail";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import Llm from "Common/Models/DatabaseModels/Llm";
|
||||
import LlmType from "Common/Types/LLM/LlmType";
|
||||
import React, { Fragment, FunctionComponent, ReactElement, useState } from "react";
|
||||
import Pill from "Common/UI/Components/Pill/Pill";
|
||||
import { Green, Red } from "Common/Types/BrandColors";
|
||||
import DropdownUtil from "Common/UI/Utils/Dropdown";
|
||||
|
||||
const LlmView: FunctionComponent<PageComponentProps> = (
|
||||
_props: PageComponentProps,
|
||||
): ReactElement => {
|
||||
const [modelId] = useState<ObjectID>(Navigation.getLastParamAsObjectID());
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{/* LLM View */}
|
||||
<CardModelDetail<Llm>
|
||||
name="LLM Details"
|
||||
cardProps={{
|
||||
title: "LLM Details",
|
||||
description: "Here are more details for this LLM configuration.",
|
||||
}}
|
||||
isEditable={true}
|
||||
formSteps={[
|
||||
{
|
||||
title: "Basic Info",
|
||||
id: "basic-info",
|
||||
},
|
||||
{
|
||||
title: "Provider Settings",
|
||||
id: "provider-settings",
|
||||
},
|
||||
]}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
stepId: "basic-info",
|
||||
title: "Name",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
placeholder: "My OpenAI GPT-4",
|
||||
validation: {
|
||||
minLength: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: {
|
||||
description: true,
|
||||
},
|
||||
title: "Description",
|
||||
stepId: "basic-info",
|
||||
fieldType: FormFieldSchemaType.LongText,
|
||||
required: false,
|
||||
placeholder: "GPT-4 for general AI features.",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
llmType: true,
|
||||
},
|
||||
title: "LLM Provider",
|
||||
stepId: "provider-settings",
|
||||
fieldType: FormFieldSchemaType.Dropdown,
|
||||
required: true,
|
||||
placeholder: "Select LLM Provider",
|
||||
dropdownOptions: DropdownUtil.getDropdownOptionsFromEnum(LlmType),
|
||||
},
|
||||
{
|
||||
field: {
|
||||
apiKey: true,
|
||||
},
|
||||
title: "API Key",
|
||||
stepId: "provider-settings",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: false,
|
||||
placeholder: "sk-...",
|
||||
description:
|
||||
"Required for OpenAI and Anthropic. Not required for Ollama if self-hosted.",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
modelName: true,
|
||||
},
|
||||
title: "Model Name",
|
||||
stepId: "provider-settings",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: false,
|
||||
placeholder: "gpt-4, claude-3-opus, llama2",
|
||||
description:
|
||||
"The specific model to use (e.g., gpt-4, claude-3-opus, llama2).",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
baseUrl: true,
|
||||
},
|
||||
title: "Base URL",
|
||||
stepId: "provider-settings",
|
||||
fieldType: FormFieldSchemaType.URL,
|
||||
required: false,
|
||||
placeholder: "http://localhost:11434",
|
||||
description:
|
||||
"Required for Ollama. Optional for others to use custom endpoints.",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isEnabled: true,
|
||||
},
|
||||
title: "Enabled",
|
||||
stepId: "provider-settings",
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
required: false,
|
||||
description: "Enable or disable this LLM configuration.",
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
modelType: Llm,
|
||||
id: "model-detail-llm",
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
_id: true,
|
||||
},
|
||||
title: "LLM ID",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: "Name",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
description: true,
|
||||
},
|
||||
title: "Description",
|
||||
placeholder: "No description provided.",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
llmType: true,
|
||||
},
|
||||
title: "Provider",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
modelName: true,
|
||||
},
|
||||
title: "Model Name",
|
||||
placeholder: "Not specified",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
baseUrl: true,
|
||||
},
|
||||
title: "Base URL",
|
||||
placeholder: "Not specified (using default)",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isEnabled: true,
|
||||
},
|
||||
title: "Status",
|
||||
fieldType: FieldType.Boolean,
|
||||
getElement: (item: Llm): ReactElement => {
|
||||
if (item.isEnabled) {
|
||||
return <Pill text="Enabled" color={Green} />;
|
||||
}
|
||||
return <Pill text="Disabled" color={Red} />;
|
||||
},
|
||||
},
|
||||
],
|
||||
modelId: modelId,
|
||||
}}
|
||||
/>
|
||||
|
||||
<ModelDelete
|
||||
modelType={Llm}
|
||||
modelId={modelId}
|
||||
onDeleteSuccess={() => {
|
||||
Navigation.navigate(RouteMap[PageMap.SETTINGS_LLMS] as Route);
|
||||
}}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default LlmView;
|
||||
275
Dashboard/src/Pages/Settings/Llms.tsx
Normal file
275
Dashboard/src/Pages/Settings/Llms.tsx
Normal file
@@ -0,0 +1,275 @@
|
||||
import ProjectUtil from "Common/UI/Utils/Project";
|
||||
import PageComponentProps from "../PageComponentProps";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import Banner from "Common/UI/Components/Banner/Banner";
|
||||
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
|
||||
import ModelTable from "Common/UI/Components/ModelTable/ModelTable";
|
||||
import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import { APP_API_URL } from "Common/UI/Config";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import Llm from "Common/Models/DatabaseModels/Llm";
|
||||
import LlmType from "Common/Types/LLM/LlmType";
|
||||
import React, { Fragment, FunctionComponent, ReactElement } from "react";
|
||||
import Pill from "Common/UI/Components/Pill/Pill";
|
||||
import { Green, Red } from "Common/Types/BrandColors";
|
||||
import DropdownUtil from "Common/UI/Utils/Dropdown";
|
||||
|
||||
const LlmPage: FunctionComponent<PageComponentProps> = (): ReactElement => {
|
||||
return (
|
||||
<Fragment>
|
||||
<>
|
||||
<ModelTable<Llm>
|
||||
modelType={Llm}
|
||||
id="global-llms-table"
|
||||
name="Settings > Global LLMs"
|
||||
userPreferencesKey={"settings-global-llms-table"}
|
||||
isDeleteable={false}
|
||||
isEditable={false}
|
||||
isCreateable={false}
|
||||
cardProps={{
|
||||
title: "Global LLMs",
|
||||
description:
|
||||
"Global LLMs are configured by your administrator and are available to all projects for AI features.",
|
||||
}}
|
||||
fetchRequestOptions={{
|
||||
overrideRequestUrl: URL.fromString(APP_API_URL.toString()).addRoute(
|
||||
"/llm/global-llms",
|
||||
),
|
||||
}}
|
||||
noItemsMessage={"No global LLMs configured."}
|
||||
showRefreshButton={true}
|
||||
filters={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: "Name",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
llmType: true,
|
||||
},
|
||||
title: "Provider",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
]}
|
||||
columns={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: "Name",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
llmType: true,
|
||||
},
|
||||
title: "Provider",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
modelName: true,
|
||||
},
|
||||
title: "Model",
|
||||
type: FieldType.Text,
|
||||
noValueMessage: "-",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<Banner
|
||||
openInNewTab={true}
|
||||
title="Need help with setting up Custom LLMs?"
|
||||
description="Here is a guide which will help you get set up with your own LLM configurations."
|
||||
link={Route.fromString("/docs/ai/llm")}
|
||||
hideOnMobile={true}
|
||||
/>
|
||||
|
||||
<ModelTable<Llm>
|
||||
modelType={Llm}
|
||||
query={{
|
||||
projectId: ProjectUtil.getCurrentProjectId()!,
|
||||
}}
|
||||
id="project-llms-table"
|
||||
userPreferencesKey={"settings-project-llms-table"}
|
||||
name="Settings > LLMs"
|
||||
isDeleteable={true}
|
||||
isEditable={true}
|
||||
isViewable={true}
|
||||
isCreateable={true}
|
||||
cardProps={{
|
||||
title: "Project LLMs",
|
||||
description:
|
||||
"Configure LLMs (Large Language Models) for AI features. Connect to OpenAI, Anthropic, Ollama, or other providers.",
|
||||
}}
|
||||
selectMoreFields={{
|
||||
apiKey: true,
|
||||
}}
|
||||
noItemsMessage={
|
||||
"No LLMs configured. Add an LLM to enable AI features for your project."
|
||||
}
|
||||
viewPageRoute={Navigation.getCurrentRoute()}
|
||||
formSteps={[
|
||||
{
|
||||
title: "Basic Info",
|
||||
id: "basic-info",
|
||||
},
|
||||
{
|
||||
title: "Provider Settings",
|
||||
id: "provider-settings",
|
||||
},
|
||||
]}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
stepId: "basic-info",
|
||||
title: "Name",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
placeholder: "My OpenAI GPT-4",
|
||||
validation: {
|
||||
minLength: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: {
|
||||
description: true,
|
||||
},
|
||||
title: "Description",
|
||||
stepId: "basic-info",
|
||||
fieldType: FormFieldSchemaType.LongText,
|
||||
required: false,
|
||||
placeholder: "GPT-4 for general AI features.",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
llmType: true,
|
||||
},
|
||||
title: "LLM Provider",
|
||||
stepId: "provider-settings",
|
||||
fieldType: FormFieldSchemaType.Dropdown,
|
||||
required: true,
|
||||
placeholder: "Select LLM Provider",
|
||||
dropdownOptions: DropdownUtil.getDropdownOptionsFromEnum(LlmType),
|
||||
},
|
||||
{
|
||||
field: {
|
||||
apiKey: true,
|
||||
},
|
||||
title: "API Key",
|
||||
stepId: "provider-settings",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: false,
|
||||
placeholder: "sk-...",
|
||||
description:
|
||||
"Required for OpenAI and Anthropic. Not required for Ollama if self-hosted.",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
modelName: true,
|
||||
},
|
||||
title: "Model Name",
|
||||
stepId: "provider-settings",
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: false,
|
||||
placeholder: "gpt-4, claude-3-opus, llama2",
|
||||
description:
|
||||
"The specific model to use (e.g., gpt-4, claude-3-opus, llama2).",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
baseUrl: true,
|
||||
},
|
||||
title: "Base URL",
|
||||
stepId: "provider-settings",
|
||||
fieldType: FormFieldSchemaType.URL,
|
||||
required: false,
|
||||
placeholder: "http://localhost:11434",
|
||||
description:
|
||||
"Required for Ollama. Optional for others to use custom endpoints.",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isEnabled: true,
|
||||
},
|
||||
title: "Enabled",
|
||||
stepId: "provider-settings",
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
required: false,
|
||||
description: "Enable or disable this LLM configuration.",
|
||||
},
|
||||
]}
|
||||
showRefreshButton={true}
|
||||
filters={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: "Name",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
llmType: true,
|
||||
},
|
||||
title: "Provider",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isEnabled: true,
|
||||
},
|
||||
title: "Enabled",
|
||||
type: FieldType.Boolean,
|
||||
},
|
||||
]}
|
||||
columns={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: "Name",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
llmType: true,
|
||||
},
|
||||
title: "Provider",
|
||||
type: FieldType.Text,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
modelName: true,
|
||||
},
|
||||
title: "Model",
|
||||
type: FieldType.Text,
|
||||
noValueMessage: "-",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isEnabled: true,
|
||||
},
|
||||
title: "Status",
|
||||
type: FieldType.Boolean,
|
||||
getElement: (item: Llm): ReactElement => {
|
||||
if (item.isEnabled) {
|
||||
return <Pill text="Enabled" color={Green} />;
|
||||
}
|
||||
return <Pill text="Disabled" color={Red} />;
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default LlmPage;
|
||||
@@ -360,6 +360,15 @@ const DashboardSideMenu: () => JSX.Element = (): ReactElement => {
|
||||
},
|
||||
icon: IconProp.Signal,
|
||||
},
|
||||
{
|
||||
link: {
|
||||
title: "LLMs",
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS_LLMS] as Route,
|
||||
),
|
||||
},
|
||||
icon: IconProp.Robot,
|
||||
},
|
||||
{
|
||||
link: {
|
||||
title: "Domains",
|
||||
|
||||
@@ -79,6 +79,16 @@ const SettingProbes: LazyExoticComponent<FunctionComponent<ComponentProps>> =
|
||||
return import("../Pages/Settings/Probes");
|
||||
});
|
||||
|
||||
const SettingLlms: LazyExoticComponent<FunctionComponent<ComponentProps>> =
|
||||
lazy(() => {
|
||||
return import("../Pages/Settings/Llms");
|
||||
});
|
||||
|
||||
const SettingsLlmView: LazyExoticComponent<FunctionComponent<ComponentProps>> =
|
||||
lazy(() => {
|
||||
return import("../Pages/Settings/LlmView");
|
||||
});
|
||||
|
||||
const SettingFeatureFlags: LazyExoticComponent<
|
||||
FunctionComponent<ComponentProps>
|
||||
> = lazy(() => {
|
||||
@@ -1028,6 +1038,18 @@ const SettingsRoutes: FunctionComponent<ComponentProps> = (
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_LLMS)}
|
||||
element={
|
||||
<Suspense fallback={Loader}>
|
||||
<SettingLlms
|
||||
{...props}
|
||||
pageRoute={RouteMap[PageMap.SETTINGS_LLMS] as Route}
|
||||
/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_TEAMS)}
|
||||
element={
|
||||
@@ -1087,6 +1109,18 @@ const SettingsRoutes: FunctionComponent<ComponentProps> = (
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_LLM_VIEW, 2)}
|
||||
element={
|
||||
<Suspense fallback={Loader}>
|
||||
<SettingsLlmView
|
||||
{...props}
|
||||
pageRoute={RouteMap[PageMap.SETTINGS_LLM_VIEW] as Route}
|
||||
/>
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
</PageRoute>
|
||||
</Routes>
|
||||
);
|
||||
|
||||
@@ -363,6 +363,10 @@ enum PageMap {
|
||||
// Probes.
|
||||
SETTINGS_PROBES = "SETTINGS_PROBES",
|
||||
|
||||
// LLMs.
|
||||
SETTINGS_LLMS = "SETTINGS_LLMS",
|
||||
SETTINGS_LLM_VIEW = "SETTINGS_LLM_VIEW",
|
||||
|
||||
// SSO.
|
||||
SETTINGS_SSO = "SETTINGS_SSO",
|
||||
|
||||
|
||||
@@ -280,6 +280,8 @@ export const SettingsRoutePath: Dictionary<string> = {
|
||||
[PageMap.SETTINGS_PROBE_VIEW]: `probes/${RouteParams.ModelID}`,
|
||||
[PageMap.SETTINGS_LABELS]: "labels",
|
||||
[PageMap.SETTINGS_PROBES]: "probes",
|
||||
[PageMap.SETTINGS_LLMS]: "llm",
|
||||
[PageMap.SETTINGS_LLM_VIEW]: `llm/${RouteParams.ModelID}`,
|
||||
};
|
||||
|
||||
export const OnCallDutyRoutePath: Dictionary<string> = {
|
||||
@@ -1937,6 +1939,18 @@ const RouteMap: Dictionary<Route> = {
|
||||
}`,
|
||||
),
|
||||
|
||||
// LLMs.
|
||||
[PageMap.SETTINGS_LLMS]: new Route(
|
||||
`/dashboard/${RouteParams.ProjectID}/settings/${
|
||||
SettingsRoutePath[PageMap.SETTINGS_LLMS]
|
||||
}`,
|
||||
),
|
||||
[PageMap.SETTINGS_LLM_VIEW]: new Route(
|
||||
`/dashboard/${RouteParams.ProjectID}/settings/${
|
||||
SettingsRoutePath[PageMap.SETTINGS_LLM_VIEW]
|
||||
}`,
|
||||
),
|
||||
|
||||
// workflows.
|
||||
[PageMap.WORKFLOWS_ROOT]: new Route(
|
||||
`/dashboard/${RouteParams.ProjectID}/workflows/*`,
|
||||
|
||||
Reference in New Issue
Block a user