Skip to content

Commit

Permalink
Merge branch 'develop' into plugin-birdeye
Browse files Browse the repository at this point in the history
  • Loading branch information
simpletrontdip authored Dec 27, 2024
2 parents bf99d6a + c9be71e commit 7561961
Show file tree
Hide file tree
Showing 393 changed files with 11,512 additions and 5,258 deletions.
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -340,3 +340,7 @@ STORY_PRIVATE_KEY= # Story private key
STORY_API_BASE_URL= # Story API base URL
STORY_API_KEY= # Story API key
PINATA_JWT= # Pinata JWT for uploading files to IPFS

# Cronos zkEVM
CRONOSZKEVM_ADDRESS=
CRONOSZKEVM_PRIVATE_KEY=
81 changes: 81 additions & 0 deletions .github/workflows/jsdoc-automation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: JSDoc Automation

on:
workflow_dispatch:
inputs:
pull_number:
description: 'Pull Request Number (if not provided, scans root_directory) - PR must be merged to develop branch'
required: false
type: string
root_directory:
description: 'Only scans files in this directory (relative to repository root, e.g., packages/core/src)'
required: true
default: 'packages/core/src/test_resources'
type: string
excluded_directories:
description: 'Directories to exclude from scanning (comma-separated, relative to root_directory)'
required: true
default: 'node_modules,dist,test'
type: string
reviewers:
description: 'Pull Request Reviewers (comma-separated GitHub usernames)'
required: true
default: ''
type: string

jobs:
generate-docs:
runs-on: ubuntu-latest

env:
GITHUB_ACCESS_TOKEN: ${{ secrets.GH_PAT }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '23'

- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 8
run_install: false

- name: Update lockfile
working-directory: packages/jsdoc-automation
run: |
echo "Updating lockfile..."
pnpm install --no-frozen-lockfile
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
git add pnpm-lock.yaml
git commit -m "chore: update pnpm lockfile" || echo "No changes to commit"
git push || echo "No changes to push"
- name: Install root dependencies
run: pnpm install --no-frozen-lockfile

- name: Install package dependencies
working-directory: packages/jsdoc-automation
run: pnpm install --no-frozen-lockfile

- name: Run documentation generator
working-directory: packages/jsdoc-automation
run: |
echo "Node version: $(node --version)"
echo "NPM version: $(npm --version)"
echo "Directory contents:"
ls -la
NODE_OPTIONS='--experimental-vm-modules --no-warnings' pnpm start
env:
INPUT_ROOT_DIRECTORY: ${{ inputs.root_directory }}
INPUT_PULL_NUMBER: ${{ inputs.pull_number }}
INPUT_EXCLUDED_DIRECTORIES: ${{ inputs.excluded_directories }}
INPUT_REVIEWERS: ${{ inputs.reviewers }}
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ packages/core/src/providers/cache
packages/core/src/providers/cache/*
cache/*
packages/plugin-coinbase/src/plugins/transactions.csv
packages/plugin-coinbase/package-lock.json

tsup.config.bundled_*.mjs

Expand Down
3 changes: 3 additions & 0 deletions agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@
"@elizaos/plugin-near": "workspace:*",
"@elizaos/plugin-zksync-era": "workspace:*",
"@elizaos/plugin-birdeye": "workspace:*",
"@elizaos/plugin-twitter": "workspace:*",
"@elizaos/plugin-cronoszkevm": "workspace:*",
"@elizaos/plugin-3d-generation": "workspace:*",
"readline": "1.3.0",
"ws": "8.18.0",
"yargs": "17.7.2"
Expand Down
25 changes: 25 additions & 0 deletions agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { evmPlugin } from "@elizaos/plugin-evm";
import { storyPlugin } from "@elizaos/plugin-story";
import { flowPlugin } from "@elizaos/plugin-flow";
import { imageGenerationPlugin } from "@elizaos/plugin-image-generation";
import { ThreeDGenerationPlugin } from "@elizaos/plugin-3d-generation";
import { multiversxPlugin } from "@elizaos/plugin-multiversx";
import { nearPlugin } from "@elizaos/plugin-near";
import { nftGenerationPlugin } from "@elizaos/plugin-nft-generation";
Expand All @@ -56,6 +57,7 @@ import { TEEMode, teePlugin } from "@elizaos/plugin-tee";
import { tonPlugin } from "@elizaos/plugin-ton";
import { zksyncEraPlugin } from "@elizaos/plugin-zksync-era";
import { birdeyePlugin } from "@elizaos/plugin-birdeye";
import { cronosZkEVMPlugin } from "@elizaos/plugin-cronoszkevm";
import { abstractPlugin } from "@elizaos/plugin-abstract";
import Database from "better-sqlite3";
import fs from "fs";
Expand Down Expand Up @@ -179,6 +181,25 @@ export async function loadCharacters(
const character = JSON.parse(content);
validateCharacterConfig(character);

// .id isn't really valid
const characterId = character.id || character.name;
const characterPrefix = `CHARACTER.${characterId.toUpperCase().replace(/ /g, "_")}.`;

const characterSettings = Object.entries(process.env)
.filter(([key]) => key.startsWith(characterPrefix))
.reduce((settings, [key, value]) => {
const settingKey = key.slice(characterPrefix.length);
return { ...settings, [settingKey]: value };
}, {});

if (Object.keys(characterSettings).length > 0) {
character.settings = character.settings || {};
character.settings.secrets = {
...characterSettings,
...character.settings.secrets,
};
}

// Handle plugins
if (isAllStrings(character.plugins)) {
elizaLogger.info("Plugins are: ", character.plugins);
Expand Down Expand Up @@ -517,6 +538,7 @@ export async function createAgent(
getSecret(character, "HEURIST_API_KEY")
? imageGenerationPlugin
: null,
getSecret(character, "FAL_API_KEY") ? ThreeDGenerationPlugin : null,
...(getSecret(character, "COINBASE_API_KEY") &&
getSecret(character, "COINBASE_PRIVATE_KEY")
? [
Expand Down Expand Up @@ -545,6 +567,9 @@ export async function createAgent(
getSecret(character, "APTOS_PRIVATE_KEY") ? aptosPlugin : null,
getSecret(character, "MVX_PRIVATE_KEY") ? multiversxPlugin : null,
getSecret(character, "ZKSYNC_PRIVATE_KEY") ? zksyncEraPlugin : null,
getSecret(character, "CRONOSZKEVM_PRIVATE_KEY")
? cronosZkEVMPlugin
: null,
getSecret(character, "TON_PRIVATE_KEY") ? tonPlugin : null,
getSecret(character, "SUI_PRIVATE_KEY") ? suiPlugin : null,
getSecret(character, "STORY_PRIVATE_KEY") ? storyPlugin : null,
Expand Down
82 changes: 69 additions & 13 deletions client/src/Chat.tsx
Original file line number Diff line number Diff line change
@@ -1,55 +1,75 @@
import { useState } from "react";
import { useRef, useState } from "react";
import { useParams } from "react-router-dom";
import { useMutation } from "@tanstack/react-query";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { ImageIcon } from "lucide-react";
import { Input } from "@/components/ui/input";
import "./App.css";
import path from "path";

type TextResponse = {
text: string;
user: string;
attachments?: { url: string; contentType: string; title: string }[];
};

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

const mutation = useMutation({
mutationFn: async (text: string) => {
const formData = new FormData();
formData.append("text", text);
formData.append("userId", "user");
formData.append("roomId", `default-room-${agentId}`);

if (selectedFile) {
formData.append("file", selectedFile);
}

const res = await fetch(`/api/${agentId}/message`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
text,
userId: "user",
roomId: `default-room-${agentId}`,
}),
body: formData,
});
return res.json() as Promise<TextResponse[]>;
},
onSuccess: (data) => {
setMessages((prev) => [...prev, ...data]);
setSelectedFile(null);
},
});

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!input.trim()) return;
if (!input.trim() && !selectedFile) return;

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

mutation.mutate(input);
setInput("");
};

const handleFileSelect = () => {
fileInputRef.current?.click();
};

const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file && file.type.startsWith('image/')) {
setSelectedFile(file);
}
};

return (
<div className="flex flex-col h-screen max-h-screen w-full">
<div className="flex-1 min-h-0 overflow-y-auto p-4">
Expand All @@ -58,7 +78,7 @@ export default function Chat() {
messages.map((message, index) => (
<div
key={index}
className={`flex ${
className={`text-left flex ${
message.user === "user"
? "justify-end"
: "justify-start"
Expand All @@ -72,7 +92,22 @@ export default function Chat() {
}`}
>
{message.text}
</div>
{message.attachments?.map((attachment, i) => (
attachment.contentType.startsWith('image/') && (
<img
key={i}
src={message.user === "user"
? attachment.url
: attachment.url.startsWith('http')
? attachment.url
: `http://localhost:3000/media/generated/${attachment.url.split('/').pop()}`
}
alt={attachment.title || "Attached image"}
className="mt-2 max-w-full rounded-lg"
/>
)
))}
</div>
</div>
))
) : (
Expand All @@ -86,17 +121,38 @@ export default function Chat() {
<div className="border-t p-4 bg-background">
<div className="max-w-3xl mx-auto">
<form onSubmit={handleSubmit} className="flex gap-2">
<input
type="file"
ref={fileInputRef}
onChange={handleFileChange}
accept="image/*"
className="hidden"
/>
<Input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Type a message..."
className="flex-1"
disabled={mutation.isPending}
/>
<Button
type="button"
variant="outline"
size="icon"
onClick={handleFileSelect}
disabled={mutation.isPending}
>
<ImageIcon className="h-4 w-4" />
</Button>
<Button type="submit" disabled={mutation.isPending}>
{mutation.isPending ? "..." : "Send"}
</Button>
</form>
{selectedFile && (
<div className="mt-2 text-sm text-muted-foreground">
Selected file: {selectedFile.name}
</div>
)}
</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ You can run Grok models by setting the `XAI_MODEL` environment variable to `grok

### Run with OpenAI

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

## Additional Requirements

Expand Down
Loading

0 comments on commit 7561961

Please sign in to comment.