Skip to content

Commit 074ae50

Browse files
authored
Merge branch 'develop' into feature/archetype-selector
2 parents 88c88d0 + 486c9d4 commit 074ae50

File tree

49 files changed

+5747
-999
lines changed

Some content is hidden

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

49 files changed

+5747
-999
lines changed

.env.example

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ SUPABASE_ANON_KEY=
1919
REMOTE_CHARACTER_URLS=
2020

2121
# Logging
22-
DEFAULT_LOG_LEVEL=info
23-
LOG_JSON_FORMAT= # Print everything in logger as json; false by default
22+
DEFAULT_LOG_LEVEL=warn
23+
LOG_JSON_FORMAT=false # Print everything in logger as json; false by default
2424

2525
###############################
2626
#### Client Configurations ####

.github/workflows/smoke-tests.yml

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ jobs:
1212
runs-on: ubuntu-latest
1313
container:
1414
image: node:23-bullseye
15+
env:
16+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
1517
steps:
1618
- uses: actions/checkout@v4
1719

agent/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
"@elizaos/plugin-node": "workspace:*",
6464
"@elizaos/plugin-solana": "workspace:*",
6565
"@elizaos/plugin-injective": "workspace:*",
66-
"@elizaos/plugin-solana-agentkit": "workspace:*",
66+
"@elizaos/plugin-solana-agent-kit": "workspace:*",
6767
"@elizaos/plugin-squid-router": "workspace:*",
6868
"@elizaos/plugin-autonome": "workspace:*",
6969
"@elizaos/plugin-starknet": "workspace:*",

agent/src/index.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ import { openWeatherPlugin } from "@elizaos/plugin-open-weather";
8585
import { quaiPlugin } from "@elizaos/plugin-quai";
8686
import { sgxPlugin } from "@elizaos/plugin-sgx";
8787
import { solanaPlugin } from "@elizaos/plugin-solana";
88-
import { solanaAgentkitPlugin } from "@elizaos/plugin-solana-agentkit";
88+
import { solanaAgentkitPlugin } from "@elizaos/plugin-solana-agent-kit";
8989
import { squidRouterPlugin } from "@elizaos/plugin-squid-router";
9090
import { stargazePlugin } from "@elizaos/plugin-stargaze";
9191
import { storyPlugin } from "@elizaos/plugin-story";
@@ -197,7 +197,7 @@ export async function loadCharacterFromOnchain(): Promise<Character[]> {
197197
const jsonText = onchainJson;
198198

199199
console.log("JSON:", jsonText);
200-
if (jsonText == "null") return [];
200+
if (!jsonText) return [];
201201
const loadedCharacters = [];
202202
try {
203203
const character = JSON.parse(jsonText);
@@ -1200,7 +1200,7 @@ const startAgents = async () => {
12001200
characters = await loadCharacterFromOnchain();
12011201
}
12021202

1203-
if ((onchainJson == "null" && charactersArg) || hasValidRemoteUrls()) {
1203+
if ((!onchainJson && charactersArg) || hasValidRemoteUrls()) {
12041204
characters = await loadCharacters(charactersArg);
12051205
}
12061206

client/src/components/chat.tsx

+18-9
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
} from "@/components/ui/chat/chat-bubble";
77
import { ChatInput } from "@/components/ui/chat/chat-input";
88
import { ChatMessageList } from "@/components/ui/chat/chat-message-list";
9-
import { useTransition, animated } from "@react-spring/web";
9+
import { useTransition, animated, AnimatedProps } from "@react-spring/web";
1010
import { Paperclip, Send, X } from "lucide-react";
1111
import { useEffect, useRef, useState } from "react";
1212
import { Content, UUID } from "@elizaos/core";
@@ -23,14 +23,18 @@ import { IAttachment } from "@/types";
2323
import { AudioRecorder } from "./audio-recorder";
2424
import { Badge } from "./ui/badge";
2525

26-
interface ExtraContentFields {
26+
type ExtraContentFields = {
2727
user: string;
2828
createdAt: number;
2929
isLoading?: boolean;
30-
}
30+
};
3131

3232
type ContentWithUser = Content & ExtraContentFields;
3333

34+
type AnimatedDivProps = AnimatedProps<{ style: React.CSSProperties }> & {
35+
children?: React.ReactNode;
36+
};
37+
3438
export default function Page({ agentId }: { agentId: UUID }) {
3539
const { toast } = useToast();
3640
const [selectedFile, setSelectedFile] = useState<File | null>(null);
@@ -67,7 +71,6 @@ export default function Page({ agentId }: { agentId: UUID }) {
6771
}
6872
};
6973

70-
7174
const handleSendMessage = (e: React.FormEvent<HTMLFormElement>) => {
7275
e.preventDefault();
7376
if (!input) return;
@@ -167,17 +170,23 @@ export default function Page({ agentId }: { agentId: UUID }) {
167170
leave: { opacity: 0, transform: "translateY(10px)" },
168171
});
169172

173+
const CustomAnimatedDiv = animated.div as React.FC<AnimatedDivProps>;
174+
170175
return (
171176
<div className="flex flex-col w-full h-[calc(100dvh)] p-4">
172177
<div className="flex-1 overflow-y-auto">
173178
<ChatMessageList ref={messagesContainerRef}>
174179
{transitions((styles, message) => {
175180
const variant = getMessageVariant(message?.user);
176181
return (
177-
// @ts-expect-error
178-
<animated.div
179-
style={styles}
180-
className="flex flex-col gap-2 p-4"
182+
<CustomAnimatedDiv
183+
style={{
184+
...styles,
185+
display: "flex",
186+
flexDirection: "column",
187+
gap: "0.5rem",
188+
padding: "1rem",
189+
}}
181190
>
182191
<ChatBubble
183192
variant={variant}
@@ -266,7 +275,7 @@ export default function Page({ agentId }: { agentId: UUID }) {
266275
</div>
267276
</div>
268277
</ChatBubble>
269-
</animated.div>
278+
</CustomAnimatedDiv>
270279
);
271280
})}
272281
</ChatMessageList>

client/src/routes/overview.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ import { type UUID } from "@elizaos/core";
77
export default function AgentRoute() {
88
const { agentId } = useParams<{ agentId: UUID }>();
99

10-
if (!agentId) return <div>No data.</div>;
11-
1210
const query = useQuery({
1311
queryKey: ["agent", agentId],
14-
queryFn: () => apiClient.getAgent(agentId),
12+
queryFn: () => apiClient.getAgent(agentId ?? ""),
1513
refetchInterval: 5_000,
14+
enabled: Boolean(agentId),
1615
});
1716

17+
if (!agentId) return <div>No data.</div>;
18+
1819
const character = query?.data?.character;
1920

2021
if (!character) return null;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2+
import { RedisClient } from '../src';
3+
import { type UUID, elizaLogger } from '@elizaos/core';
4+
import Redis from 'ioredis';
5+
6+
// Mock ioredis
7+
vi.mock('ioredis', () => {
8+
const MockRedis = vi.fn(() => ({
9+
on: vi.fn(),
10+
get: vi.fn(),
11+
set: vi.fn(),
12+
del: vi.fn(),
13+
quit: vi.fn()
14+
}));
15+
return { default: MockRedis };
16+
});
17+
18+
// Mock elizaLogger
19+
vi.mock('@elizaos/core', async () => {
20+
const actual = await vi.importActual('@elizaos/core');
21+
return {
22+
...actual as any,
23+
elizaLogger: {
24+
success: vi.fn(),
25+
error: vi.fn()
26+
}
27+
};
28+
});
29+
30+
describe('RedisClient', () => {
31+
let client: RedisClient;
32+
let mockRedis: any;
33+
34+
beforeEach(() => {
35+
vi.clearAllMocks();
36+
client = new RedisClient('redis://localhost:6379');
37+
// Get the instance created by the constructor
38+
mockRedis = (Redis as unknown as ReturnType<typeof vi.fn>).mock.results[0].value;
39+
});
40+
41+
afterEach(() => {
42+
vi.clearAllMocks();
43+
});
44+
45+
describe('constructor', () => {
46+
it('should set up event handlers', () => {
47+
expect(mockRedis.on).toHaveBeenCalledWith('connect', expect.any(Function));
48+
expect(mockRedis.on).toHaveBeenCalledWith('error', expect.any(Function));
49+
});
50+
51+
it('should log success on connect', () => {
52+
const connectHandler = mockRedis.on.mock.calls.find(call => call[0] === 'connect')[1];
53+
connectHandler();
54+
expect(elizaLogger.success).toHaveBeenCalledWith('Connected to Redis');
55+
});
56+
57+
it('should log error on error event', () => {
58+
const error = new Error('Redis connection error');
59+
const errorHandler = mockRedis.on.mock.calls.find(call => call[0] === 'error')[1];
60+
errorHandler(error);
61+
expect(elizaLogger.error).toHaveBeenCalledWith('Redis error:', error);
62+
});
63+
});
64+
65+
describe('getCache', () => {
66+
const agentId = 'test-agent' as UUID;
67+
const key = 'test-key';
68+
const expectedRedisKey = `${agentId}:${key}`;
69+
70+
it('should return cached value when it exists', async () => {
71+
const cachedValue = 'cached-data';
72+
mockRedis.get.mockResolvedValueOnce(cachedValue);
73+
74+
const result = await client.getCache({ agentId, key });
75+
76+
expect(mockRedis.get).toHaveBeenCalledWith(expectedRedisKey);
77+
expect(result).toBe(cachedValue);
78+
});
79+
80+
it('should return undefined when key does not exist', async () => {
81+
mockRedis.get.mockResolvedValueOnce(null);
82+
83+
const result = await client.getCache({ agentId, key });
84+
85+
expect(mockRedis.get).toHaveBeenCalledWith(expectedRedisKey);
86+
expect(result).toBeUndefined();
87+
});
88+
89+
it('should handle errors and return undefined', async () => {
90+
const error = new Error('Redis error');
91+
mockRedis.get.mockRejectedValueOnce(error);
92+
93+
const result = await client.getCache({ agentId, key });
94+
95+
expect(mockRedis.get).toHaveBeenCalledWith(expectedRedisKey);
96+
expect(elizaLogger.error).toHaveBeenCalledWith('Error getting cache:', error);
97+
expect(result).toBeUndefined();
98+
});
99+
});
100+
101+
describe('setCache', () => {
102+
const agentId = 'test-agent' as UUID;
103+
const key = 'test-key';
104+
const value = 'test-value';
105+
const expectedRedisKey = `${agentId}:${key}`;
106+
107+
it('should successfully set cache value', async () => {
108+
mockRedis.set.mockResolvedValueOnce('OK');
109+
110+
const result = await client.setCache({ agentId, key, value });
111+
112+
expect(mockRedis.set).toHaveBeenCalledWith(expectedRedisKey, value);
113+
expect(result).toBe(true);
114+
});
115+
116+
it('should handle errors and return false', async () => {
117+
const error = new Error('Redis error');
118+
mockRedis.set.mockRejectedValueOnce(error);
119+
120+
const result = await client.setCache({ agentId, key, value });
121+
122+
expect(mockRedis.set).toHaveBeenCalledWith(expectedRedisKey, value);
123+
expect(elizaLogger.error).toHaveBeenCalledWith('Error setting cache:', error);
124+
expect(result).toBe(false);
125+
});
126+
});
127+
128+
describe('deleteCache', () => {
129+
const agentId = 'test-agent' as UUID;
130+
const key = 'test-key';
131+
const expectedRedisKey = `${agentId}:${key}`;
132+
133+
it('should successfully delete cache when key exists', async () => {
134+
mockRedis.del.mockResolvedValueOnce(1);
135+
136+
const result = await client.deleteCache({ agentId, key });
137+
138+
expect(mockRedis.del).toHaveBeenCalledWith(expectedRedisKey);
139+
expect(result).toBe(true);
140+
});
141+
142+
it('should return false when key does not exist', async () => {
143+
mockRedis.del.mockResolvedValueOnce(0);
144+
145+
const result = await client.deleteCache({ agentId, key });
146+
147+
expect(mockRedis.del).toHaveBeenCalledWith(expectedRedisKey);
148+
expect(result).toBe(false);
149+
});
150+
151+
it('should handle errors and return false', async () => {
152+
const error = new Error('Redis error');
153+
mockRedis.del.mockRejectedValueOnce(error);
154+
155+
const result = await client.deleteCache({ agentId, key });
156+
157+
expect(mockRedis.del).toHaveBeenCalledWith(expectedRedisKey);
158+
expect(elizaLogger.error).toHaveBeenCalledWith('Error deleting cache:', error);
159+
expect(result).toBe(false);
160+
});
161+
});
162+
163+
describe('disconnect', () => {
164+
it('should successfully disconnect from Redis', async () => {
165+
mockRedis.quit.mockResolvedValueOnce('OK');
166+
167+
await client.disconnect();
168+
169+
expect(mockRedis.quit).toHaveBeenCalled();
170+
expect(elizaLogger.success).toHaveBeenCalledWith('Disconnected from Redis');
171+
});
172+
173+
it('should handle disconnect errors', async () => {
174+
const error = new Error('Redis disconnect error');
175+
mockRedis.quit.mockRejectedValueOnce(error);
176+
177+
await client.disconnect();
178+
179+
expect(mockRedis.quit).toHaveBeenCalled();
180+
expect(elizaLogger.error).toHaveBeenCalledWith('Error disconnecting from Redis:', error);
181+
});
182+
});
183+
});

packages/adapter-redis/package.json

+5-2
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,15 @@
2424
},
2525
"devDependencies": {
2626
"@types/ioredis": "^5.0.0",
27-
"tsup": "8.3.5"
27+
"tsup": "8.3.5",
28+
"vitest": "^3.0.2"
2829
},
2930
"scripts": {
3031
"build": "tsup --format esm --dts",
3132
"dev": "tsup --format esm --dts --watch",
32-
"lint": "eslint --fix --cache ."
33+
"lint": "eslint --fix --cache .",
34+
"test": "vitest run",
35+
"test:watch": "vitest"
3336
},
3437
"peerDependencies": {
3538
"whatwg-url": "7.1.0"
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { defineConfig } from 'vitest/config';
2+
3+
export default defineConfig({
4+
test: {
5+
globals: true,
6+
environment: 'node',
7+
},
8+
});

0 commit comments

Comments
 (0)