Skip to content

Commit

Permalink
feat: Implement character loading from multiple paths and enhance API… (
Browse files Browse the repository at this point in the history
#2365)

* feat: Implement character loading from multiple paths and enhance API for character management

* fix: Update character handling to prioritize username and email fields

- Modified the AgentRuntime class to use character.username instead of character.name for user identification.
- Enhanced user account creation logic to utilize character.email and character.name for better user data management.
- Updated Character type definition to include optional email field.
  • Loading branch information
tercel authored Jan 18, 2025
1 parent 90585eb commit 5b57a6d
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 119 deletions.
121 changes: 61 additions & 60 deletions agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ export async function loadCharacterFromOnchain(): Promise<Character[]> {
async function loadCharacterFromUrl(url: string): Promise<Character> {
const response = await fetch(url);
const character = await response.json();
return jsonToCharacter(url, character);
return await jsonToCharacter(url, character);
}

async function jsonToCharacter(
Expand Down Expand Up @@ -305,6 +305,61 @@ async function loadCharacter(filePath: string): Promise<Character> {
return jsonToCharacter(filePath, character);
}

async function loadCharacterTryPath(characterPath: string): Promise<Character> {
let content: string | null = null;
let resolvedPath = "";

// Try different path resolutions in order
const pathsToTry = [
characterPath, // exact path as specified
path.resolve(process.cwd(), characterPath), // relative to cwd
path.resolve(process.cwd(), "agent", characterPath), // Add this
path.resolve(__dirname, characterPath), // relative to current script
path.resolve(__dirname, "characters", path.basename(characterPath)), // relative to agent/characters
path.resolve(__dirname, "../characters", path.basename(characterPath)), // relative to characters dir from agent
path.resolve(
__dirname,
"../../characters",
path.basename(characterPath)
), // relative to project root characters dir
];

elizaLogger.info(
"Trying paths:",
pathsToTry.map((p) => ({
path: p,
exists: fs.existsSync(p),
}))
);

for (const tryPath of pathsToTry) {
content = tryLoadFile(tryPath);
if (content !== null) {
resolvedPath = tryPath;
break;
}
}

if (content === null) {
elizaLogger.error(
`Error loading character from ${characterPath}: File not found in any of the expected locations`
);
elizaLogger.error("Tried the following paths:");
pathsToTry.forEach((p) => elizaLogger.error(` - ${p}`));
throw new Error(
`Error loading character from ${characterPath}: File not found in any of the expected locations`
);
}
try {
const character: Character = await loadCharacter(resolvedPath);
elizaLogger.info(`Successfully loaded character from: ${resolvedPath}`);
return character;
} catch (e) {
elizaLogger.error(`Error parsing character from ${resolvedPath}: ${e}`);
throw new Error(`Error parsing character from ${resolvedPath}: ${e}`);
}
}

function commaSeparatedStringToArray(commaSeparated: string): string[] {
return commaSeparated?.split(",").map((value) => value.trim());
}
Expand All @@ -317,68 +372,11 @@ export async function loadCharacters(

if (characterPaths?.length > 0) {
for (const characterPath of characterPaths) {
let content: string | null = null;
let resolvedPath = "";

// Try different path resolutions in order
const pathsToTry = [
characterPath, // exact path as specified
path.resolve(process.cwd(), characterPath), // relative to cwd
path.resolve(process.cwd(), "agent", characterPath), // Add this
path.resolve(__dirname, characterPath), // relative to current script
path.resolve(
__dirname,
"characters",
path.basename(characterPath)
), // relative to agent/characters
path.resolve(
__dirname,
"../characters",
path.basename(characterPath)
), // relative to characters dir from agent
path.resolve(
__dirname,
"../../characters",
path.basename(characterPath)
), // relative to project root characters dir
];

elizaLogger.info(
"Trying paths:",
pathsToTry.map((p) => ({
path: p,
exists: fs.existsSync(p),
}))
);

for (const tryPath of pathsToTry) {
content = tryLoadFile(tryPath);
if (content !== null) {
resolvedPath = tryPath;
break;
}
}

if (content === null) {
elizaLogger.error(
`Error loading character from ${characterPath}: File not found in any of the expected locations`
);
elizaLogger.error("Tried the following paths:");
pathsToTry.forEach((p) => elizaLogger.error(` - ${p}`));
process.exit(1);
}

try {
const character: Character = await loadCharacter(resolvedPath);

const character: Character =
await loadCharacterTryPath(characterPath);
loadedCharacters.push(character);
elizaLogger.info(
`Successfully loaded character from: ${resolvedPath}`
);
} catch (e) {
elizaLogger.error(
`Error parsing character from ${resolvedPath}: ${e}`
);
process.exit(1);
}
}
Expand Down Expand Up @@ -1233,6 +1231,9 @@ const startAgents = async () => {
return startAgent(character, directClient);
};

directClient.loadCharacterTryPath = loadCharacterTryPath;
directClient.jsonToCharacter = jsonToCharacter;

directClient.start(serverPort);

if (serverPort !== Number.parseInt(settings.SERVER_PORT || "3000")) {
Expand Down
97 changes: 76 additions & 21 deletions packages/client-direct/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
type UUID,
validateCharacterConfig,
ServiceType,
Character,
} from "@elizaos/core";

import type { TeeLogQuery, TeeLogService } from "@elizaos/plugin-tee-log";
Expand Down Expand Up @@ -114,9 +115,8 @@ export function createApiRouter(
if (agent) {
agent.stop();
directClient.unregisterAgent(agent);
res.status(204).send();
}
else {
res.status(204).json({ success: true });
} else {
res.status(404).json({ error: "Agent not found" });
}
});
Expand Down Expand Up @@ -269,18 +269,20 @@ export function createApiRouter(

for (const agentRuntime of agents.values()) {
const teeLogService = agentRuntime
.getService<TeeLogService>(
ServiceType.TEE_LOG
)
.getInstance();
.getService<TeeLogService>(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<TeeLogService>(ServiceType.TEE_LOG).getInstance();
const attestation = await teeLogService.generateAttestation(JSON.stringify(allAgents));
const teeLogService = runtime
.getService<TeeLogService>(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);
Expand All @@ -300,13 +302,13 @@ export function createApiRouter(
}

const teeLogService = agentRuntime
.getService<TeeLogService>(
ServiceType.TEE_LOG
)
.getInstance();
.getService<TeeLogService>(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);
Expand Down Expand Up @@ -335,12 +337,16 @@ export function createApiRouter(
};
const agentRuntime: AgentRuntime = agents.values().next().value;
const teeLogService = agentRuntime
.getService<TeeLogService>(
ServiceType.TEE_LOG
)
.getService<TeeLogService>(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,
Expand All @@ -354,6 +360,55 @@ export function createApiRouter(
}
);

router.post("/agent/start", async (req, res) => {
const { characterPath, characterJson } = req.body;
console.log("characterPath:", characterPath);
console.log("characterJson:", characterJson);
try {
let character: Character;
if (characterJson) {
character = await directClient.jsonToCharacter(
characterPath,
characterJson
);
} else if (characterPath) {
character =
await directClient.loadCharacterTryPath(characterPath);
} else {
throw new Error("No character path or JSON provided");
}
await directClient.startAgent(character);
elizaLogger.log(`${character.name} started`);

res.json({
id: character.id,
character: character,
});
} catch (e) {
elizaLogger.error(`Error parsing character: ${e}`);
res.status(400).json({
error: e.message,
});
return;
}
});

router.post("/agents/:agentId/stop", async (req, res) => {
const agentId = req.params.agentId;
console.log("agentId", agentId);
const agent: AgentRuntime = agents.get(agentId);

// update character
if (agent) {
// stop agent
agent.stop();
directClient.unregisterAgent(agent);
// if it has a different name, the agentId will change
res.json({ success: true });
} else {
res.status(404).json({ error: "Agent not found" });
}
});

return router;
}

69 changes: 37 additions & 32 deletions packages/client-direct/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ export class DirectClient {
private agents: Map<string, AgentRuntime>; // container management
private server: any; // Store server instance

Check warning on line 114 in packages/client-direct/src/index.ts

View check run for this annotation

codefactor.io / CodeFactor

packages/client-direct/src/index.ts#L114

Unexpected any. Specify a different type. (@typescript-eslint/no-explicit-any)
public startAgent: Function; // Store startAgent functor
public loadCharacterTryPath: Function; // Store loadCharacterTryPath functor
public jsonToCharacter: Function; // Store jsonToCharacter functor

constructor() {
elizaLogger.log("DirectClient constructor");
Expand All @@ -136,7 +138,6 @@ export class DirectClient {
const apiRouter = createApiRouter(this.agents, this);
this.app.use(apiRouter);


const apiLogRouter = createVerifiableLogApiRouter(this.agents);
this.app.use(apiLogRouter);

Expand Down Expand Up @@ -555,38 +556,42 @@ export class DirectClient {
content: contentObj,
};

runtime.messageManager.createMemory(responseMessage).then(() => {
const messageId = stringToUuid(Date.now().toString());
const memory: Memory = {
id: messageId,
agentId: runtime.agentId,
userId,
roomId,
content,
createdAt: Date.now(),
};

// run evaluators (generally can be done in parallel with processActions)
// can an evaluator modify memory? it could but currently doesn't
runtime.evaluate(memory, state).then(() => {
// only need to call if responseMessage.content.action is set
if (contentObj.action) {
// pass memory (query) to any actions to call
runtime.processActions(
memory,
[responseMessage],
state,
async (_newMessages) => {
// FIXME: this is supposed override what the LLM said/decided
// but the promise doesn't make this possible
//message = newMessages;
return [memory];
}
); // 0.674s
}
resolve(true);
runtime.messageManager
.createMemory(responseMessage)
.then(() => {
const messageId = stringToUuid(
Date.now().toString()
);
const memory: Memory = {
id: messageId,
agentId: runtime.agentId,
userId,
roomId,
content,
createdAt: Date.now(),
};

// run evaluators (generally can be done in parallel with processActions)
// can an evaluator modify memory? it could but currently doesn't
runtime.evaluate(memory, state).then(() => {
// only need to call if responseMessage.content.action is set
if (contentObj.action) {
// pass memory (query) to any actions to call
runtime.processActions(
memory,
[responseMessage],
state,
async (_newMessages) => {
// FIXME: this is supposed override what the LLM said/decided
// but the promise doesn't make this possible
//message = newMessages;
return [memory];
}
); // 0.674s
}
resolve(true);
});
});
});
});
res.json({ response: hfOut });
}
Expand Down
Loading

0 comments on commit 5b57a6d

Please sign in to comment.