diff --git a/client/src/App.tsx b/client/src/App.tsx
index e7c13846c4f..9aff62f9176 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -9,6 +9,7 @@ import Chat from "./routes/chat";
import Overview from "./routes/overview";
import Home from "./routes/home";
import useVersion from "./hooks/use-version";
+import { ArchetypeSelector } from "./components/archetype-selector";
const queryClient = new QueryClient({
defaultOptions: {
@@ -40,6 +41,10 @@ function App() {
path="chat/:agentId"
element={}
/>
+ }
+ />
}
diff --git a/client/src/components/app-sidebar.tsx b/client/src/components/app-sidebar.tsx
index 1f7f949ca72..530d83e3b3f 100644
--- a/client/src/components/app-sidebar.tsx
+++ b/client/src/components/app-sidebar.tsx
@@ -1,3 +1,4 @@
+import React from "react";
import { useQuery } from "@tanstack/react-query";
import info from "@/lib/info.json";
import {
@@ -73,22 +74,36 @@ export function AppSidebar() {
{agents?.map(
(agent: { id: UUID; name: string }) => (
-
-
+
+
+
+
+
+ {agent.name}
+
+
+
+
+
-
-
-
- {agent.name}
-
-
-
-
+
+ Change
+ Agent's Archetype
+
+
+
+
)
)}
diff --git a/client/src/components/archetype-selector.tsx b/client/src/components/archetype-selector.tsx
new file mode 100644
index 00000000000..03aa06830c3
--- /dev/null
+++ b/client/src/components/archetype-selector.tsx
@@ -0,0 +1,43 @@
+import React, { useState } from "react";
+import { ArchetypeName } from "../types/archetypes";
+import { useParams } from "react-router";
+import { type UUID } from "@elizaos/core";
+import ArcheTypeButtons from "./ui/archetype/archetype-buttons";
+import ArchetypeDisplay from "./ui/archetype/archetype-display";
+import ArchetypeSelection from "./ui/archetype/archetype-selection";
+import ArchetypeApplyStatus from "./ui/archetype/archetype-apply-status";
+import ArchetypeNoAgent from "./ui/archetype/archetype-no-agent";
+
+export const ArchetypeSelector: React.FC = () => {
+ const { agentId } = useParams<{ agentId: UUID }>();
+
+ if (!agentId) {
+ return ;
+ }
+
+ const [selectedArchetype, setSelectedArchetype] =
+ useState(null);
+ const [applyStatus, setApplyStatus] = useState(null);
+
+ return (
+
+
+ Character Archetype Selector
+
+
+
+
+ {selectedArchetype && (
+
+ )}
+
+
+
+
+
+ );
+};
diff --git a/client/src/components/ui/archetype/archetype-apply-status.tsx b/client/src/components/ui/archetype/archetype-apply-status.tsx
new file mode 100644
index 00000000000..04301ba3ea0
--- /dev/null
+++ b/client/src/components/ui/archetype/archetype-apply-status.tsx
@@ -0,0 +1,19 @@
+type ArchetypeApplyStatusProps = {
+ applyStatus: string | null;
+};
+
+const ArchetypeApplyStatus = ({ applyStatus }: ArchetypeApplyStatusProps) => {
+ let message;
+ let containerClass;
+ if (applyStatus === "success") {
+ message = "Archetype applied successfully!";
+ containerClass = "text-green-600";
+ } else {
+ message = "Failed to apply archetype. Please try again";
+ containerClass = "text-red-600";
+ }
+
+ return {message}
;
+};
+
+export default ArchetypeApplyStatus;
diff --git a/client/src/components/ui/archetype/archetype-buttons.tsx b/client/src/components/ui/archetype/archetype-buttons.tsx
new file mode 100644
index 00000000000..949aadc94b9
--- /dev/null
+++ b/client/src/components/ui/archetype/archetype-buttons.tsx
@@ -0,0 +1,85 @@
+import { apiClient } from "@/lib/api";
+import { ArchetypeName, archetypes } from "@/types/archetypes";
+import { type UUID } from "@elizaos/core";
+import { Button } from "../button";
+import { useState } from "react";
+
+type ArcheTypeButtonsProps = {
+ agentId: UUID;
+ selectedArchetype: ArchetypeName | null;
+ setApplyStatus: (status: string | null) => void;
+};
+
+const ArcheTypeButtons = ({
+ agentId,
+ selectedArchetype,
+ setApplyStatus,
+}: ArcheTypeButtonsProps) => {
+ const [isApplying, setIsApplying] = useState(false);
+
+ const handleApply = async () => {
+ if (!selectedArchetype) return;
+
+ if (
+ !window.confirm(
+ `Are you sure you want to apply the ${selectedArchetype} archetype?`
+ )
+ )
+ return;
+
+ try {
+ setIsApplying(true);
+ await apiClient.applyArchetype(
+ agentId as UUID,
+ archetypes[selectedArchetype]
+ );
+ setApplyStatus("success");
+ } catch (error) {
+ console.error("Failed to apply archetype:", error);
+ setApplyStatus(
+ `Error: ${error instanceof Error ? error.message : "Unknown error"}`
+ );
+ } finally {
+ setIsApplying(false);
+ }
+
+ setTimeout(() => setApplyStatus(null), 3000);
+ };
+
+ const handleDownload = () => {
+ if (!selectedArchetype) return;
+
+ const blob = new Blob(
+ [JSON.stringify(archetypes[selectedArchetype], null, 2)],
+ { type: "application/json" }
+ );
+ const url = URL.createObjectURL(blob);
+ const link = document.createElement("a");
+ link.href = url;
+ link.download = `${selectedArchetype}.json`;
+ link.click();
+ URL.revokeObjectURL(url);
+ };
+
+ return (
+
+
+
+
+
+ );
+};
+
+export default ArcheTypeButtons;
diff --git a/client/src/components/ui/archetype/archetype-display.tsx b/client/src/components/ui/archetype/archetype-display.tsx
new file mode 100644
index 00000000000..46a73f6c658
--- /dev/null
+++ b/client/src/components/ui/archetype/archetype-display.tsx
@@ -0,0 +1,16 @@
+import { ArchetypeName, archetypes } from "@/types/archetypes";
+
+type ArchetypeDisplayProps = {
+ selectedArchetype: ArchetypeName;
+};
+
+const ArchetypeDisplay = ({ selectedArchetype }: ArchetypeDisplayProps) => (
+
+
Preview
+
+ {JSON.stringify(archetypes[selectedArchetype], null, 2)}
+
+
+);
+
+export default ArchetypeDisplay;
diff --git a/client/src/components/ui/archetype/archetype-no-agent.tsx b/client/src/components/ui/archetype/archetype-no-agent.tsx
new file mode 100644
index 00000000000..33ff3a0b5db
--- /dev/null
+++ b/client/src/components/ui/archetype/archetype-no-agent.tsx
@@ -0,0 +1,7 @@
+const ArchetypeNoAgent = () => (
+
+ Agent ID not found, Archetype Selector not available.
+
+);
+
+export default ArchetypeNoAgent;
diff --git a/client/src/components/ui/archetype/archetype-selection.tsx b/client/src/components/ui/archetype/archetype-selection.tsx
new file mode 100644
index 00000000000..4b5b016cd4d
--- /dev/null
+++ b/client/src/components/ui/archetype/archetype-selection.tsx
@@ -0,0 +1,31 @@
+import { ArchetypeName } from "@/types/archetypes";
+
+type ArchetypeSelectionProps = {
+ setSelectedArchetype: (selectedArchetype: ArchetypeName) => void;
+};
+
+const ArchetypeSelection = ({
+ setSelectedArchetype,
+}: ArchetypeSelectionProps) => (
+
+
+
+
+);
+
+export default ArchetypeSelection;
diff --git a/client/src/lib/api.ts b/client/src/lib/api.ts
index d24e69c8e92..a57b83f2120 100644
--- a/client/src/lib/api.ts
+++ b/client/src/lib/api.ts
@@ -102,4 +102,10 @@ export const apiClient = {
body: formData,
});
},
+ applyArchetype: (agentId: string, archetype: Character) =>
+ fetcher({
+ url: `/agents/${agentId}/set`,
+ method: "POST",
+ body: archetype,
+ }),
};
diff --git a/client/src/types/archetypes.ts b/client/src/types/archetypes.ts
new file mode 100644
index 00000000000..272f54c5273
--- /dev/null
+++ b/client/src/types/archetypes.ts
@@ -0,0 +1,124 @@
+import { Character, ModelProviderName } from "../../../packages/core/src/types";
+
+export enum ArchetypeName {
+ Friendly = "Friendly",
+ Sarcastic = "Sarcastic",
+ Formal = "Formal",
+}
+
+export const archetypes: Record = {
+ [ArchetypeName.Friendly]: {
+ name: "FriendlyBot",
+ modelProvider: ModelProviderName.ANTHROPIC,
+ settings: {
+ voice: { model: "en_US-lessac-medium" },
+ },
+ bio: ["A friendly bot who always looks on the bright side."],
+ style: {
+ all: ["Optimistic", "Encouraging", "Kind"],
+ chat: [],
+ post: [],
+ },
+ knowledge: ["Basic human etiquette", "Empathy strategies"],
+ messageExamples: [
+ [{ user: "{{user1}}", content: { text: "Hello!" } }],
+ [
+ {
+ user: "FriendlyBot",
+ content: { text: "Hi there! How can I brighten your day?" },
+ },
+ ],
+ ],
+ postExamples: ["Stay positive! Every day is a new opportunity!"],
+ lore: [
+ "A cheerful assistant who spreads positivity and joy in every interaction.",
+ ],
+ topics: ["Positive thinking", "Encouragement", "Empathy"],
+ adjectives: ["Optimistic", "Cheerful", "Supportive", "Warm"],
+ clients: [],
+ plugins: [],
+ },
+ [ArchetypeName.Sarcastic]: {
+ name: "SarcasticBot",
+ modelProvider: ModelProviderName.ANTHROPIC,
+ settings: {
+ voice: { model: "en_US-amy-medium" },
+ },
+ bio: ["A bot with a sharp tongue and dry humor."],
+ style: {
+ all: ["Witty", "Cynical", "Dry"],
+ chat: ["Witty", "Cynical", "Dry"],
+ post: ["Witty", "Cynical", "Dry"],
+ },
+ knowledge: ["Pop culture references", "Puns and witty remarks"],
+ messageExamples: [
+ [
+ {
+ user: "{{user1}}",
+ content: { text: "What’s the weather like?" },
+ },
+ ],
+ [
+ {
+ user: "SarcasticBot",
+ content: {
+ text: "Oh, it’s just perfect for staying indoors and questioning your life choices.",
+ },
+ },
+ ],
+ ],
+ postExamples: ["Life is a joke, and I’m the punchline."],
+ lore: ["A quick-witted assistant with a penchant for humor and irony."],
+ topics: ["Pop culture", "Humor", "Satire"],
+ adjectives: ["Witty", "Cynical", "Dry", "Sharp"],
+ clients: [],
+ plugins: [],
+ },
+ [ArchetypeName.Formal]: {
+ name: "FormalBot",
+ modelProvider: ModelProviderName.ANTHROPIC,
+ settings: {
+ voice: { model: "en_US-ryan-medium" },
+ },
+ bio: [
+ "A professional and courteous bot with a refined communication style.",
+ ],
+ style: {
+ all: ["Polite", "Professional", "Articulate"],
+ chat: ["Polite", "Professional", "Articulate"],
+ post: ["Polite", "Professional", "Articulate"],
+ },
+ knowledge: ["Business etiquette", "Formal writing conventions"],
+ messageExamples: [
+ [
+ {
+ user: "{{user1}}",
+ content: { text: "Can you assist me with a task?" },
+ },
+ ],
+ [
+ {
+ user: "FormalBot",
+ content: {
+ text: "Certainly. Please provide the necessary details, and I will assist you to the best of my ability.",
+ },
+ },
+ ],
+ ],
+ postExamples: [
+ "Remember, professionalism and politeness pave the way to effective communication.",
+ "A thoughtful approach often leads to the best outcomes.",
+ ],
+ lore: [
+ "Experienced in formal communication and professional environments.",
+ ],
+ topics: [
+ "Business communication",
+ "Professional development",
+ "Etiquette",
+ ],
+ adjectives: ["Polite", "Courteous", "Respectful", "Detailed"],
+ clients: [],
+ plugins: [],
+ },
+};
diff --git a/packages/client-direct/src/api.ts b/packages/client-direct/src/api.ts
index ff97d23f0e7..43b4e28fa92 100644
--- a/packages/client-direct/src/api.ts
+++ b/packages/client-direct/src/api.ts
@@ -115,8 +115,7 @@ export function createApiRouter(
agent.stop();
directClient.unregisterAgent(agent);
res.status(204).send();
- }
- else {
+ } else {
res.status(404).json({ error: "Agent not found" });
}
});
@@ -269,18 +268,20 @@ export function createApiRouter(
for (const agentRuntime of agents.values()) {
const teeLogService = agentRuntime
- .getService(
- ServiceType.TEE_LOG
- )
- .getInstance();
+ .getService(ServiceType.TEE_LOG)
+ .getInstance();
const agents = await teeLogService.getAllAgents();
- allAgents.push(...agents)
+ allAgents.push(...agents);
}
const runtime: AgentRuntime = agents.values().next().value;
- const teeLogService = runtime.getService(ServiceType.TEE_LOG).getInstance();
- const attestation = await teeLogService.generateAttestation(JSON.stringify(allAgents));
+ const teeLogService = runtime
+ .getService(ServiceType.TEE_LOG)
+ .getInstance();
+ const attestation = await teeLogService.generateAttestation(
+ JSON.stringify(allAgents)
+ );
res.json({ agents: allAgents, attestation: attestation });
} catch (error) {
elizaLogger.error("Failed to get TEE agents:", error);
@@ -300,13 +301,13 @@ export function createApiRouter(
}
const teeLogService = agentRuntime
- .getService(
- ServiceType.TEE_LOG
- )
- .getInstance();
+ .getService(ServiceType.TEE_LOG)
+ .getInstance();
const teeAgent = await teeLogService.getAgent(agentId);
- const attestation = await teeLogService.generateAttestation(JSON.stringify(teeAgent));
+ const attestation = await teeLogService.generateAttestation(
+ JSON.stringify(teeAgent)
+ );
res.json({ agent: teeAgent, attestation: attestation });
} catch (error) {
elizaLogger.error("Failed to get TEE agent:", error);
@@ -335,12 +336,16 @@ export function createApiRouter(
};
const agentRuntime: AgentRuntime = agents.values().next().value;
const teeLogService = agentRuntime
- .getService(
- ServiceType.TEE_LOG
- )
+ .getService(ServiceType.TEE_LOG)
.getInstance();
- const pageQuery = await teeLogService.getLogs(teeLogQuery, page, pageSize);
- const attestation = await teeLogService.generateAttestation(JSON.stringify(pageQuery));
+ const pageQuery = await teeLogService.getLogs(
+ teeLogQuery,
+ page,
+ pageSize
+ );
+ const attestation = await teeLogService.generateAttestation(
+ JSON.stringify(pageQuery)
+ );
res.json({
logs: pageQuery,
attestation: attestation,
@@ -356,4 +361,3 @@ export function createApiRouter(
return router;
}
-