Skip to content

Commit

Permalink
feat: Artifacts Capability for Agents
Browse files Browse the repository at this point in the history
  • Loading branch information
danny-avila committed Feb 11, 2025
1 parent 8369557 commit cf0ad7b
Show file tree
Hide file tree
Showing 12 changed files with 167 additions and 11 deletions.
3 changes: 3 additions & 0 deletions api/models/schema/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ const agentSchema = mongoose.Schema(
model_parameters: {
type: Object,
},
artifacts: {
type: String,
},
access_level: {
type: Number,
},
Expand Down
18 changes: 18 additions & 0 deletions api/server/services/Endpoints/agents/initialize.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const getBedrockOptions = require('~/server/services/Endpoints/bedrock/options')
const initOpenAI = require('~/server/services/Endpoints/openAI/initialize');
const initCustom = require('~/server/services/Endpoints/custom/initialize');
const initGoogle = require('~/server/services/Endpoints/google/initialize');
const generateArtifactsPrompt = require('~/app/clients/prompts/artifacts');
const { getCustomEndpointConfig } = require('~/server/services/Config');
const { loadAgentTools } = require('~/server/services/ToolService');
const AgentClient = require('~/server/controllers/agents/client');
Expand Down Expand Up @@ -72,6 +73,16 @@ const primeResources = async (_attachments, _tool_resources) => {
}
};

/**
* @param {object} params
* @param {ServerRequest} params.req
* @param {ServerResponse} params.res
* @param {Agent} params.agent
* @param {object} [params.endpointOption]
* @param {AgentToolResources} [params.tool_resources]
* @param {boolean} [params.isInitialAgent]
* @returns {Promise<Agent>}
*/
const initializeAgentOptions = async ({
req,
res,
Expand Down Expand Up @@ -132,6 +143,13 @@ const initializeAgentOptions = async ({
agent.model_parameters.model = agent.model;
}

if (typeof agent.artifacts === 'string' && agent.artifacts !== '') {
agent.additional_instructions = generateArtifactsPrompt({
endpoint: agent.provider,
artifacts: agent.artifacts,
});
}

const tokensModel =
agent.provider === EModelEndpoint.azureOpenAI ? agent.model : agent.model_parameters.model;

Expand Down
4 changes: 2 additions & 2 deletions client/src/common/agents-types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AgentCapabilities } from 'librechat-data-provider';
import { AgentCapabilities, ArtifactModes } from 'librechat-data-provider';
import type { Agent, AgentProvider, AgentModelParameters } from 'librechat-data-provider';
import type { OptionWithIcon, ExtendedFile } from './types';

Expand All @@ -9,7 +9,6 @@ export type TAgentOption = OptionWithIcon &
};

export type TAgentCapabilities = {
[AgentCapabilities.artifacts]: boolean;
[AgentCapabilities.file_search]: boolean;
[AgentCapabilities.execute_code]: boolean;
[AgentCapabilities.end_after_tools]?: boolean;
Expand All @@ -27,4 +26,5 @@ export type AgentForm = {
tools?: string[];
provider?: AgentProvider | OptionWithIcon;
agent_ids?: string[];
[AgentCapabilities.artifacts]?: ArtifactModes | string;
} & TAgentCapabilities;
3 changes: 3 additions & 0 deletions client/src/components/SidePanel/Agents/AgentConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import AgentAvatar from './AgentAvatar';
import { Spinner } from '~/components';
import FileSearch from './FileSearch';
import ShareAgent from './ShareAgent';
import Artifacts from './Artifacts';
import AgentTool from './AgentTool';
import CodeForm from './Code/Form';
import { Panel } from '~/common';
Expand Down Expand Up @@ -342,6 +343,8 @@ export default function AgentConfig({
{codeEnabled && <CodeForm agent_id={agent_id} files={code_files} />}
{/* File Search */}
{fileSearchEnabled && <FileSearch agent_id={agent_id} files={knowledge_files} />}
{/* Artifacts */}
{artifactsEnabled && <Artifacts />}
</div>
)}
{/* Agent Tools & Actions */}
Expand Down
5 changes: 4 additions & 1 deletion client/src/components/SidePanel/Agents/AgentPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export default function AgentPanel({

const {
name,
artifacts,
description,
instructions,
model: _model,
Expand All @@ -139,6 +140,7 @@ export default function AgentPanel({
agent_id,
data: {
name,
artifacts,
description,
instructions,
model,
Expand All @@ -162,6 +164,7 @@ export default function AgentPanel({

create.mutate({
name,
artifacts,
description,
instructions,
model,
Expand All @@ -184,7 +187,7 @@ export default function AgentPanel({

const canEditAgent = useMemo(() => {
const canEdit =
agentQuery.data?.isCollaborative ?? false
(agentQuery.data?.isCollaborative ?? false)
? true
: agentQuery.data?.author === user?.id || user?.role === SystemRoles.ADMIN;

Expand Down
1 change: 0 additions & 1 deletion client/src/components/SidePanel/Agents/AgentSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ export default function AgentSelect({
};

const capabilities: TAgentCapabilities = {
[AgentCapabilities.artifacts]: false,
[AgentCapabilities.file_search]: false,
[AgentCapabilities.execute_code]: false,
[AgentCapabilities.end_after_tools]: false,
Expand Down
124 changes: 124 additions & 0 deletions client/src/components/SidePanel/Agents/Artifacts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { useFormContext } from 'react-hook-form';
import { ArtifactModes, AgentCapabilities } from 'librechat-data-provider';
import type { AgentForm } from '~/common';
import {
Switch,
HoverCard,
HoverCardPortal,
HoverCardContent,
HoverCardTrigger,
} from '~/components/ui';
import { useLocalize } from '~/hooks';
import { CircleHelpIcon } from '~/components/svg';
import { ESide } from '~/common';

export default function Artifacts() {
const localize = useLocalize();
const methods = useFormContext<AgentForm>();
const { setValue, watch } = methods;

const artifactsMode = watch(AgentCapabilities.artifacts);

const handleArtifactsChange = (value: boolean) => {
setValue(AgentCapabilities.artifacts, value ? ArtifactModes.DEFAULT : '', {
shouldDirty: true,
});
};

const handleShadcnuiChange = (value: boolean) => {
setValue(AgentCapabilities.artifacts, value ? ArtifactModes.SHADCNUI : ArtifactModes.DEFAULT, {
shouldDirty: true,
});
};

const handleCustomModeChange = (value: boolean) => {
setValue(AgentCapabilities.artifacts, value ? ArtifactModes.CUSTOM : ArtifactModes.DEFAULT, {
shouldDirty: true,
});
};

const isEnabled = artifactsMode !== undefined && artifactsMode !== '';
const isCustomEnabled = artifactsMode === ArtifactModes.CUSTOM;
const isShadcnEnabled = artifactsMode === ArtifactModes.SHADCNUI;

return (
<div className="w-full">
<div className="mb-1.5 flex items-center gap-2">
<span>
<label className="text-token-text-primary block font-medium">
{localize('com_ui_artifacts')}
</label>
</span>
</div>
<div className="flex flex-col gap-3">
<SwitchItem
id="artifacts"
label={localize('com_ui_artifacts_toggle_agent')}
checked={isEnabled}
onCheckedChange={handleArtifactsChange}
hoverCardText={localize('com_nav_info_code_artifacts_agent')}
/>
<SwitchItem
id="includeShadcnui"
label={localize('com_ui_include_shadcnui_agent')}
checked={isShadcnEnabled}
onCheckedChange={handleShadcnuiChange}
hoverCardText={localize('com_nav_info_include_shadcnui')}
disabled={!isEnabled || isCustomEnabled}
/>
<SwitchItem
id="customPromptMode"
label={localize('com_ui_custom_prompt_mode')}
checked={isCustomEnabled}
onCheckedChange={handleCustomModeChange}
hoverCardText={localize('com_nav_info_custom_prompt_mode')}
disabled={!isEnabled}
/>
</div>
</div>
);
}

function SwitchItem({
id,
label,
checked,
onCheckedChange,
hoverCardText,
disabled = false,
}: {
id: string;
label: string;
checked: boolean;
onCheckedChange: (value: boolean) => void;
hoverCardText: string;
disabled?: boolean;
}) {
return (
<HoverCard openDelay={50}>
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<div className={disabled ? 'text-text-tertiary' : ''}>{label}</div>
<HoverCardTrigger>
<CircleHelpIcon className="h-4 w-4 text-text-tertiary" />
</HoverCardTrigger>
</div>
<HoverCardPortal>
<HoverCardContent side={ESide.Top} className="w-80">
<div className="space-y-2">
<p className="text-sm text-text-secondary">{hoverCardText}</p>
</div>
</HoverCardContent>
</HoverCardPortal>
<Switch
id={id}
checked={checked}
onCheckedChange={onCheckedChange}
className="ml-4"
data-testid={id}
disabled={disabled}
/>
</div>
</HoverCard>
);
}
2 changes: 1 addition & 1 deletion client/src/components/SidePanel/Agents/Code/Action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export default function Action({ authType = '', isToolAuthenticated = false }) {
</button>
)}
<HoverCardTrigger>
<CircleHelpIcon className="h-5 w-5 text-gray-500" />
<CircleHelpIcon className="h-4 w-4 text-text-tertiary" />
</HoverCardTrigger>
</div>
<HoverCardPortal>
Expand Down
5 changes: 2 additions & 3 deletions client/src/components/SidePanel/Agents/FileSearchCheckbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default function FileSearchCheckbox() {
{...field}
checked={field.value}
onCheckedChange={field.onChange}
className="relative float-left mr-2 inline-flex h-4 w-4 cursor-pointer"
className="relative float-left mr-2 inline-flex h-4 w-4 cursor-pointer"
value={field.value.toString()}
/>
)}
Expand All @@ -38,7 +38,6 @@ export default function FileSearchCheckbox() {
type="button"
className="flex items-center space-x-2"
onClick={() =>

setValue(AgentCapabilities.file_search, !getValues(AgentCapabilities.file_search), {
shouldDirty: true,
})
Expand All @@ -51,7 +50,7 @@ export default function FileSearchCheckbox() {
{localize('com_agents_enable_file_search')}
</label>
<HoverCardTrigger>
<CircleHelpIcon className="h-5 w-5 text-gray-500" />
<CircleHelpIcon className="h-4 w-4 text-text-tertiary" />
</HoverCardTrigger>
</button>
<HoverCardPortal>
Expand Down
5 changes: 4 additions & 1 deletion client/src/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,7 @@
"com_nav_help_faq": "Help & FAQ",
"com_nav_hide_panel": "Hide right-most side panel",
"com_nav_info_code_artifacts": "Enables the display of experimental code artifacts next to the chat",
"com_nav_info_code_artifacts_agent": "Enables the use of code artifacts for this agent. By default, additional instructions specific to the use of artifacts are added, unless \"Custom Prompt Mode\" is enabled.",
"com_nav_info_custom_prompt_mode": "When enabled, the default artifacts system prompt will not be included. All artifact-generating instructions must be provided manually in this mode.",
"com_nav_info_delete_cache_storage": "This action will delete all cached TTS (Text-to-Speech) audio files stored on your device. Cached audio files are used to speed up playback of previously generated TTS audio, but they can consume storage space on your device.",
"com_nav_info_enter_to_send": "When enabled, pressing `ENTER` will send your message. When disabled, pressing Enter will add a new line, and you'll need to press `CTRL + ENTER` / `⌘ + ENTER` to send your message.",
Expand Down Expand Up @@ -509,6 +510,7 @@
"com_ui_artifact_click": "Click to open",
"com_ui_artifacts": "Artifacts",
"com_ui_artifacts_toggle": "Toggle Artifacts UI",
"com_ui_artifacts_toggle_agent": "Enable Artifacts",
"com_ui_ascending": "Asc",
"com_ui_assistant": "Assistant",
"com_ui_assistant_delete_error": "There was an error deleting the assistant",
Expand Down Expand Up @@ -687,6 +689,7 @@
"com_ui_import_conversation_info": "Import conversations from a JSON file",
"com_ui_import_conversation_success": "Conversations imported successfully",
"com_ui_include_shadcnui": "Include shadcn/ui components instructions",
"com_ui_include_shadcnui_agent": "Include shadcn/ui instructions",
"com_ui_input": "Input",
"com_ui_instructions": "Instructions",
"com_ui_latest_footer": "Every AI for Everyone.",
Expand Down Expand Up @@ -857,4 +860,4 @@
"com_ui_zoom": "Zoom",
"com_user_message": "You",
"com_warning_resubmit_unsupported": "Resubmitting the AI message is not supported for this endpoint."
}
}
1 change: 1 addition & 0 deletions packages/data-provider/src/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ export const defaultAgentFormValues = {
tools: [],
provider: {},
projectIds: [],
artifacts: '',
isCollaborative: false,
[Tools.execute_code]: false,
[Tools.file_search]: false,
Expand Down
7 changes: 5 additions & 2 deletions packages/data-provider/src/types/assistants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { AssistantsEndpoint, AgentProvider } from 'src/schemas';
import type { ContentTypes } from './runs';
import type { Agents } from './agents';
import type { TFile } from './files';
import { ArtifactModes } from 'src/artifacts';

export type Schema = OpenAPIV3.SchemaObject & { description?: string };
export type Reference = OpenAPIV3.ReferenceObject & { description?: string };
Expand Down Expand Up @@ -204,6 +205,7 @@ export type Agent = {
created_at: number;
avatar: AgentAvatar | null;
instructions: string | null;
additional_instructions?: string | null;
tools?: string[];
projectIds?: string[];
tool_kwargs?: Record<string, unknown>;
Expand All @@ -217,6 +219,7 @@ export type Agent = {
agent_ids?: string[];
end_after_tools?: boolean;
hide_sequential_outputs?: boolean;
artifacts?: ArtifactModes;
};

export type TAgentsMap = Record<string, Agent | undefined>;
Expand All @@ -231,7 +234,7 @@ export type AgentCreateParams = {
provider: AgentProvider;
model: string | null;
model_parameters: AgentModelParameters;
} & Pick<Agent, 'agent_ids' | 'end_after_tools' | 'hide_sequential_outputs'>;
} & Pick<Agent, 'agent_ids' | 'end_after_tools' | 'hide_sequential_outputs' | 'artifacts'>;

export type AgentUpdateParams = {
name?: string | null;
Expand All @@ -247,7 +250,7 @@ export type AgentUpdateParams = {
projectIds?: string[];
removeProjectIds?: string[];
isCollaborative?: boolean;
} & Pick<Agent, 'agent_ids' | 'end_after_tools' | 'hide_sequential_outputs'>;
} & Pick<Agent, 'agent_ids' | 'end_after_tools' | 'hide_sequential_outputs' | 'artifacts'>;

export type AgentListParams = {
limit?: number;
Expand Down

0 comments on commit cf0ad7b

Please sign in to comment.