Skip to content

Commit cb1f21b

Browse files
authored
Merge branch 'develop' into twitter-convo
2 parents e7dd46e + 8d7f67d commit cb1f21b

File tree

366 files changed

+24868
-27333
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

366 files changed

+24868
-27333
lines changed

.env.example

+4
Original file line numberDiff line numberDiff line change
@@ -340,3 +340,7 @@ STORY_PRIVATE_KEY= # Story private key
340340
STORY_API_BASE_URL= # Story API base URL
341341
STORY_API_KEY= # Story API key
342342
PINATA_JWT= # Pinata JWT for uploading files to IPFS
343+
344+
# Cronos zkEVM
345+
CRONOSZKEVM_ADDRESS=
346+
CRONOSZKEVM_PRIVATE_KEY=

.gitignore

-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ packages/core/src/providers/cache
4545
packages/core/src/providers/cache/*
4646
cache/*
4747
packages/plugin-coinbase/src/plugins/transactions.csv
48-
packages/plugin-coinbase/package-lock.json
4948

5049
tsup.config.bundled_*.mjs
5150

agent/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@
5353
"@elizaos/plugin-near": "workspace:*",
5454
"@elizaos/plugin-zksync-era": "workspace:*",
5555
"@elizaos/plugin-twitter": "workspace:*",
56+
"@elizaos/plugin-cronoszkevm": "workspace:*",
57+
"@elizaos/plugin-3d-generation": "workspace:*",
5658
"readline": "1.3.0",
5759
"ws": "8.18.0",
5860
"yargs": "17.7.2"

agent/src/index.ts

+25
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import { evmPlugin } from "@elizaos/plugin-evm";
4646
import { storyPlugin } from "@elizaos/plugin-story";
4747
import { flowPlugin } from "@elizaos/plugin-flow";
4848
import { imageGenerationPlugin } from "@elizaos/plugin-image-generation";
49+
import { ThreeDGenerationPlugin } from "@elizaos/plugin-3d-generation";
4950
import { multiversxPlugin } from "@elizaos/plugin-multiversx";
5051
import { nearPlugin } from "@elizaos/plugin-near";
5152
import { nftGenerationPlugin } from "@elizaos/plugin-nft-generation";
@@ -55,6 +56,7 @@ import { suiPlugin } from "@elizaos/plugin-sui";
5556
import { TEEMode, teePlugin } from "@elizaos/plugin-tee";
5657
import { tonPlugin } from "@elizaos/plugin-ton";
5758
import { zksyncEraPlugin } from "@elizaos/plugin-zksync-era";
59+
import { cronosZkEVMPlugin } from "@elizaos/plugin-cronoszkevm";
5860
import { abstractPlugin } from "@elizaos/plugin-abstract";
5961
import Database from "better-sqlite3";
6062
import fs from "fs";
@@ -178,6 +180,25 @@ export async function loadCharacters(
178180
const character = JSON.parse(content);
179181
validateCharacterConfig(character);
180182

183+
// .id isn't really valid
184+
const characterId = character.id || character.name;
185+
const characterPrefix = `CHARACTER.${characterId.toUpperCase().replace(/ /g, "_")}.`;
186+
187+
const characterSettings = Object.entries(process.env)
188+
.filter(([key]) => key.startsWith(characterPrefix))
189+
.reduce((settings, [key, value]) => {
190+
const settingKey = key.slice(characterPrefix.length);
191+
return { ...settings, [settingKey]: value };
192+
}, {});
193+
194+
if (Object.keys(characterSettings).length > 0) {
195+
character.settings = character.settings || {};
196+
character.settings.secrets = {
197+
...characterSettings,
198+
...character.settings.secrets,
199+
};
200+
}
201+
181202
// Handle plugins
182203
if (isAllStrings(character.plugins)) {
183204
elizaLogger.info("Plugins are: ", character.plugins);
@@ -516,6 +537,7 @@ export async function createAgent(
516537
getSecret(character, "HEURIST_API_KEY")
517538
? imageGenerationPlugin
518539
: null,
540+
getSecret(character, "FAL_API_KEY") ? ThreeDGenerationPlugin : null,
519541
...(getSecret(character, "COINBASE_API_KEY") &&
520542
getSecret(character, "COINBASE_PRIVATE_KEY")
521543
? [
@@ -544,6 +566,9 @@ export async function createAgent(
544566
getSecret(character, "APTOS_PRIVATE_KEY") ? aptosPlugin : null,
545567
getSecret(character, "MVX_PRIVATE_KEY") ? multiversxPlugin : null,
546568
getSecret(character, "ZKSYNC_PRIVATE_KEY") ? zksyncEraPlugin : null,
569+
getSecret(character, "CRONOSZKEVM_PRIVATE_KEY")
570+
? cronosZkEVMPlugin
571+
: null,
547572
getSecret(character, "TON_PRIVATE_KEY") ? tonPlugin : null,
548573
getSecret(character, "SUI_PRIVATE_KEY") ? suiPlugin : null,
549574
getSecret(character, "STORY_PRIVATE_KEY") ? storyPlugin : null,

client/src/Chat.tsx

+69-13
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,75 @@
1-
import { useState } from "react";
1+
import { useRef, useState } from "react";
22
import { useParams } from "react-router-dom";
33
import { useMutation } from "@tanstack/react-query";
4-
import { Input } from "@/components/ui/input";
54
import { Button } from "@/components/ui/button";
5+
import { ImageIcon } from "lucide-react";
6+
import { Input } from "@/components/ui/input";
67
import "./App.css";
8+
import path from "path";
79

810
type TextResponse = {
911
text: string;
1012
user: string;
13+
attachments?: { url: string; contentType: string; title: string }[];
1114
};
1215

1316
export default function Chat() {
1417
const { agentId } = useParams();
1518
const [input, setInput] = useState("");
1619
const [messages, setMessages] = useState<TextResponse[]>([]);
20+
const [selectedFile, setSelectedFile] = useState<File | null>(null);
21+
const fileInputRef = useRef<HTMLInputElement>(null);
1722

1823
const mutation = useMutation({
1924
mutationFn: async (text: string) => {
25+
const formData = new FormData();
26+
formData.append("text", text);
27+
formData.append("userId", "user");
28+
formData.append("roomId", `default-room-${agentId}`);
29+
30+
if (selectedFile) {
31+
formData.append("file", selectedFile);
32+
}
33+
2034
const res = await fetch(`/api/${agentId}/message`, {
2135
method: "POST",
22-
headers: {
23-
"Content-Type": "application/json",
24-
},
25-
body: JSON.stringify({
26-
text,
27-
userId: "user",
28-
roomId: `default-room-${agentId}`,
29-
}),
36+
body: formData,
3037
});
3138
return res.json() as Promise<TextResponse[]>;
3239
},
3340
onSuccess: (data) => {
3441
setMessages((prev) => [...prev, ...data]);
42+
setSelectedFile(null);
3543
},
3644
});
3745

3846
const handleSubmit = async (e: React.FormEvent) => {
3947
e.preventDefault();
40-
if (!input.trim()) return;
48+
if (!input.trim() && !selectedFile) return;
4149

4250
// Add user message immediately to state
4351
const userMessage: TextResponse = {
4452
text: input,
4553
user: "user",
54+
attachments: selectedFile ? [{ url: URL.createObjectURL(selectedFile), contentType: selectedFile.type, title: selectedFile.name }] : undefined,
4655
};
4756
setMessages((prev) => [...prev, userMessage]);
4857

4958
mutation.mutate(input);
5059
setInput("");
5160
};
5261

62+
const handleFileSelect = () => {
63+
fileInputRef.current?.click();
64+
};
65+
66+
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
67+
const file = e.target.files?.[0];
68+
if (file && file.type.startsWith('image/')) {
69+
setSelectedFile(file);
70+
}
71+
};
72+
5373
return (
5474
<div className="flex flex-col h-screen max-h-screen w-full">
5575
<div className="flex-1 min-h-0 overflow-y-auto p-4">
@@ -58,7 +78,7 @@ export default function Chat() {
5878
messages.map((message, index) => (
5979
<div
6080
key={index}
61-
className={`flex ${
81+
className={`text-left flex ${
6282
message.user === "user"
6383
? "justify-end"
6484
: "justify-start"
@@ -72,7 +92,22 @@ export default function Chat() {
7292
}`}
7393
>
7494
{message.text}
75-
</div>
95+
{message.attachments?.map((attachment, i) => (
96+
attachment.contentType.startsWith('image/') && (
97+
<img
98+
key={i}
99+
src={message.user === "user"
100+
? attachment.url
101+
: attachment.url.startsWith('http')
102+
? attachment.url
103+
: `http://localhost:3000/media/generated/${attachment.url.split('/').pop()}`
104+
}
105+
alt={attachment.title || "Attached image"}
106+
className="mt-2 max-w-full rounded-lg"
107+
/>
108+
)
109+
))}
110+
</div>
76111
</div>
77112
))
78113
) : (
@@ -86,17 +121,38 @@ export default function Chat() {
86121
<div className="border-t p-4 bg-background">
87122
<div className="max-w-3xl mx-auto">
88123
<form onSubmit={handleSubmit} className="flex gap-2">
124+
<input
125+
type="file"
126+
ref={fileInputRef}
127+
onChange={handleFileChange}
128+
accept="image/*"
129+
className="hidden"
130+
/>
89131
<Input
90132
value={input}
91133
onChange={(e) => setInput(e.target.value)}
92134
placeholder="Type a message..."
93135
className="flex-1"
94136
disabled={mutation.isPending}
95137
/>
138+
<Button
139+
type="button"
140+
variant="outline"
141+
size="icon"
142+
onClick={handleFileSelect}
143+
disabled={mutation.isPending}
144+
>
145+
<ImageIcon className="h-4 w-4" />
146+
</Button>
96147
<Button type="submit" disabled={mutation.isPending}>
97148
{mutation.isPending ? "..." : "Send"}
98149
</Button>
99150
</form>
151+
{selectedFile && (
152+
<div className="mt-2 text-sm text-muted-foreground">
153+
Selected file: {selectedFile.name}
154+
</div>
155+
)}
100156
</div>
101157
</div>
102158
</div>

docs/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ You can run Grok models by setting the `XAI_MODEL` environment variable to `grok
6767

6868
### Run with OpenAI
6969

70-
You can run OpenAI models by setting the `XAI_MODEL` environment variable to `gpt-4o-mini` or `gpt-4o`
70+
You can run OpenAI models by setting the `XAI_MODEL` environment variable to `gpt-4-mini` or `gpt-4o`
7171

7272
## Additional Requirements
7373

0 commit comments

Comments
 (0)