Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Creating Archetype Selector #2422

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
5 changes: 5 additions & 0 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down Expand Up @@ -44,6 +45,10 @@ function App() {
path="settings/:agentId"
element={<Overview />}
/>
<Route
path="settings/archetypes/:agentId"
element={<ArchetypeSelector />}
/>
alvarosps marked this conversation as resolved.
Show resolved Hide resolved
</Routes>
</div>
</SidebarInset>
Expand Down
42 changes: 27 additions & 15 deletions client/src/components/app-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,22 +73,34 @@ export function AppSidebar() {
<div>
{agents?.map(
(agent: { id: UUID; name: string }) => (
<SidebarMenuItem key={agent.id}>
<NavLink
to={`/chat/${agent.id}`}
>
<SidebarMenuButton
isActive={location.pathname.includes(
agent.id
)}
<>
<SidebarMenuItem key={agent.id}>
<NavLink
to={`/chat/${agent.id}`}
>
<User />
<span>
{agent.name}
</span>
</SidebarMenuButton>
</NavLink>
</SidebarMenuItem>
<SidebarMenuButton
isActive={location.pathname.includes(
agent.id
)}
>
<User />
<span>
{agent.name}
</span>
</SidebarMenuButton>
</NavLink>
</SidebarMenuItem>
<SidebarMenuItem>
<NavLink
to={`/settings/archetypes/${agent.id}`}
>
<SidebarMenuButton>
<Cog /> Change
Agent's Archetype
</SidebarMenuButton>
</NavLink>
</SidebarMenuItem>
</>
alvarosps marked this conversation as resolved.
Show resolved Hide resolved
)
)}
</div>
Expand Down
116 changes: 116 additions & 0 deletions client/src/components/archetype-selector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import React, { useState } from "react";
import { ArchetypeName, archetypes } from "../types/archetypes";
import { apiClient } from "@/lib/api";
import { useParams } from "react-router";
import { type UUID } from "@elizaos/core";

export const ArchetypeSelector: React.FC = () => {
const { agentId } = useParams<{ agentId: UUID }>();

const [selectedArchetype, setSelectedArchetype] =
useState<ArchetypeName | null>(null);
const [applyStatus, setApplyStatus] = useState<string | null>(null);

const handleDownload = () => {
if (!selectedArchetype) return;

const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(
JSON.stringify(archetypes[selectedArchetype], null, 2)
)}`;
const downloadAnchorNode = document.createElement("a");
downloadAnchorNode.setAttribute("href", dataStr);
downloadAnchorNode.setAttribute(
"download",
`${selectedArchetype}.json`
);
document.body.appendChild(downloadAnchorNode);
downloadAnchorNode.click();
downloadAnchorNode.remove();
};
alvarosps marked this conversation as resolved.
Show resolved Hide resolved

const handleApply = async () => {
if (!selectedArchetype) return;

try {
await apiClient.applyArchetype(
agentId,
archetypes[selectedArchetype]
);
setApplyStatus("success");
} catch (error) {
console.error("Failed to apply archetype:", error);
setApplyStatus("error");
}

setTimeout(() => setApplyStatus(null), 3000);
};
alvarosps marked this conversation as resolved.
Show resolved Hide resolved

return (
<div className="p-4">
<h2 className="text-lg font-bold mb-4">
Character Archetype Selector
</h2>
<div className="mb-4">
<label
htmlFor="archetype-select"
className="block text-sm font-medium"
>
Select an Archetype
</label>
<select
id="archetype-select"
className="mt-2 p-2 border rounded"
onChange={(e) =>
setSelectedArchetype(e.target.value as ArchetypeName)
}
>
<option value="">-- Select --</option>
{Object.keys(ArchetypeName).map((key) => (
<option key={key} value={key}>
{key}
</option>
))}
</select>
</div>

{selectedArchetype && (
<div>
<h3 className="text-md font-semibold">Preview</h3>
<pre className="p-4 bg-gray-800 text-white rounded border overflow-auto">
{JSON.stringify(archetypes[selectedArchetype], null, 2)}
</pre>
</div>
)}

<div className="flex gap-4 mt-4">
<button
onClick={handleDownload}
className="px-4 py-2 bg-blue-600 text-white rounded disabled:opacity-50"
disabled={!selectedArchetype}
>
Download Archetype
</button>

<button
onClick={handleApply}
className="px-4 py-2 bg-green-600 text-white rounded disabled:opacity-50"
disabled={!selectedArchetype || !agentId}
>
Apply Archetype
</button>
</div>

{applyStatus === "success" && (
<div className="mt-2 text-green-600">
Archetype applied successfully!
</div>
)}

{applyStatus === "error" && (
<div className="mt-2 text-red-600">
Failed to apply archetype. Please try again.
</div>
)}
</div>
);
};
6 changes: 6 additions & 0 deletions client/src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,10 @@ export const apiClient = {
body: formData,
});
},
applyArchetype: (agentId: string, archetype: Character) =>
fetcher({
url: `/agents/${agentId}/set`,
method: "POST",
body: archetype,
}),
};
124 changes: 124 additions & 0 deletions client/src/types/archetypes.ts
Original file line number Diff line number Diff line change
@@ -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, Character> = {
[ArchetypeName.Friendly]: {
name: "FriendlyBot",
modelProvider: ModelProviderName.ANTHROPIC,
settings: {
voice: { model: "en_US-happy-medium" },
alvarosps marked this conversation as resolved.
Show resolved Hide resolved
},
bio: ["A friendly bot who always looks on the bright side."],
style: {
all: ["Optimistic", "Encouraging", "Kind"],
chat: ["Optimistic", "Encouraging", "Kind"],
post: ["Optimistic", "Encouraging", "Kind"],
},
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-sarcastic-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-formal-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: [],
},
};
Loading
Loading