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: dad jokes #2092

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions packages/plugin-dad-joke/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Dad Joke Plugin for Eliza

Ask for a dad joke!

This plugin was solely made for resarech and learning purposes. Feel free to use this as reference! Happy coding ☕️

## Contributing

Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details.

## License

Built by [@pann0x](https://twitter.com/pann0x) on X.
20 changes: 20 additions & 0 deletions packages/plugin-dad-joke/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "@elizaos/plugin-dad-joke",
"version": "0.1.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"type": "module",
"scripts": {
"build": "tsup --format esm --dts",
"dev": "tsc -w",
"lint": "eslint src --ext .ts"
},
"author": "pann0x",
"license": "MIT",
"dependencies": {
"@ai16z/client-direct": "0.1.6-alpha.4",
"@ai16z/eliza": "0.1.6-alpha.4",
"@ai16z/plugin-0g": "0.1.6-alpha.4",
"dotenv": "^16.4.7"
}
}
78 changes: 78 additions & 0 deletions packages/plugin-dad-joke/src/action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import {Action, IAgentRuntime, Memory, State} from "@ai16z/eliza";
import { dadJokeActionContent, dadJokeData } from "./types.ts";
import { dadJokeProvider } from "./provider.ts";

export const getDadJokeAction: Action = {
name: "GET_DAD_JOKE",
description: "Retrieves a random dad joke",
similes: [
"DAD_JOKE",
"JOKE",
],
examples: [
[
{
user: "{{user1}}",
content: {
text: "Tell me a dad joke",
} as dadJokeActionContent,
},
{
user: "{{agentName}}",
content: {
text: "Why couldn't the bicycle stand up by itself? It was two tired.",
action: "GET_DAD_JOKE",
},
},

],
[
{
user: "{{user1}}",
content: {
text: "You got any dad jokes?",
} as dadJokeActionContent,
},
{
user: "{{agentName}}",
content: {
text: "What do you call a fake noodle? An impasta.",
action: "GET_DAD_JOKE",
},
},
],
],

validate: async (
runtime: IAgentRuntime,
message: Memory,
state?: State,
): Promise<boolean> => {
try {
const content = message.content as dadJokeActionContent;
return (
typeof content.text === "string" &&
content.text.toLowerCase().includes("joke")
);
} catch {
return false;
}
},

handler: async (
runtime: IAgentRuntime,
message: Memory,
state?: State,
): Promise<dadJokeData> => {
try {
const response = await dadJokeProvider.get(runtime, message, state);
if (!response.success) {
throw new Error(response.error);
}

return response.data;
} catch (error) {
throw new Error(error instanceof Error ? error.message : "Failed to retrieve dad joke");
}
}
}
71 changes: 71 additions & 0 deletions packages/plugin-dad-joke/src/evaluator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Evaluator, IAgentRuntime, Memory, State } from "@ai16z/eliza";
import { dadJokeEvalContent, dadJokeEvalResponse } from "./types.ts";

//the evaluator for dad jokes
//validates the response to ensure it is a dad joke
export const dadJokeEvaluator: Evaluator = {
name: "DAD_JOKE_EVALUATOR",
description: "Validates dad joke responses",
similes: [
"DAD_JOKE_CHECKER",
"JOKE_VALIDATOR",
"DAD_JOKE_RESPONSE_VALIDATOR",
],
examples: [
{
context: "Validating complete dad joke response",
messages: [
{
user: "{{user1}}",
content: {
text: "When does a joke become a dad joke? When the punchline becomes apparent.",
},
},
],
outcome: "When it becomes a groan-up.",
Comment on lines +21 to +25
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example outcome appears to be mismatched with the setup joke. The setup asks "When does a joke become a dad joke? When the punchline becomes apparent" but the outcome shows "When it becomes a groan-up". For consistency, the outcome should be "When the punchline becomes apparent" to match the setup joke's punchline.

Spotted by Graphite Reviewer

Is this helpful? React 👍 or 👎 to let us know.

},
],
validate: async (
runtime: IAgentRuntime,
message: Memory,
state?: State,
): Promise<boolean> => {
try {
const content = message.content as dadJokeEvalContent;
return typeof content.text === "string";
} catch {
return false;
}
},

handler: async (
runtime: IAgentRuntime,
message: Memory,
state?: State,
): Promise<dadJokeEvalResponse> => {
try {
const content = message.content as dadJokeEvalContent;
const text = content.text.toLowerCase();

//checks for the word "joke" in the response
if (!text.includes("joke")) {
return {
success: false,
response: "Response does not contain the word 'joke'",
};
}

return {
success: true,
response: "Dad joke response is valid",
};

} catch {
return {
success: false,
response: "Failed to validate dad joke response",
};
}
},
alwaysRun: true,
}
20 changes: 20 additions & 0 deletions packages/plugin-dad-joke/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Plugin } from "@ai16z/eliza";
import { dadJokeProvider, initializeDadJokeProvider } from "./provider.ts";
import { dadJokeEvaluator } from "./evaluator.ts";
import { dadJokeConfig } from "./types.ts";
import { getDadJokeAction } from "./action.ts";

export const dadJokePlugin: Plugin = {
name: "Dad Joke",
description: "Dad Joke Plugin for Eliza",
actions: [
getDadJokeAction
],
providers: [dadJokeProvider],
evaluators: [dadJokeEvaluator],
//rest of the plugin
}

export const initializeDadJoke = (config: dadJokeConfig): void => {
initializeDadJokeProvider(config);
}
54 changes: 54 additions & 0 deletions packages/plugin-dad-joke/src/provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { IAgentRuntime, Memory, State, Provider } from "@ai16z/eliza";
import { dadJokeProviderResponse, dadJokeData, dadJokeConfig} from "./types.ts";

let providerConfig: dadJokeConfig;

export const dadJokeProvider: Provider = {
get: async (
runtime: IAgentRuntime,
message: Memory,
state?: State,
): Promise<dadJokeProviderResponse> => {
try {
if (!providerConfig?.provider?.baseUrl) {
throw new Error("Dad Joke API url is required");
}

const baseUrl =
providerConfig.provider.baseUrl ||
"https://icanhazdadjoke.com";
Comment on lines +13 to +19
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There appears to be contradictory logic in the URL handling. The code first checks that providerConfig?.provider?.baseUrl exists (throwing if it doesn't), but then provides a fallback URL "https://icanhazdadjoke.com" anyway. Consider either:

  1. Removing the existence check since there's a default URL, or
  2. Removing the default URL if a configured URL is truly required

This will make the intended behavior more clear and eliminate unreachable code paths.

Spotted by Graphite Reviewer

Is this helpful? React 👍 or 👎 to let us know.


// Fetch dad joke
const response = await fetch(baseUrl, {
headers: {
Accept: "application/json",
},
});

if (!response.ok) {
throw new Error(`API request failed: ${response.statusText}`);
}

const data = await response.json();

// Transform API response to dadJokeData
const dadJokeData: dadJokeData = {
joke: data.joke,
};

return {
success: true,
data: dadJokeData,
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Failed to retrieve dad joke",
};
}
}
}

export const initializeDadJokeProvider = (config: dadJokeConfig): void => {
providerConfig = config;
}
33 changes: 33 additions & 0 deletions packages/plugin-dad-joke/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Content } from '@ai16z/eliza';

export interface dadJokeConfig {
provider: {
apiKey: string;
baseUrl?: string;
options?: string;
};
}
Comment on lines +3 to +9
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The apiKey field in dadJokeConfig is marked as required but isn't used in the provider implementation. Since the API appears to be public and keyless, this field should either be removed or marked as optional with ? to match the actual requirements.

Spotted by Graphite Reviewer

Is this helpful? React 👍 or 👎 to let us know.


export interface dadJokeData {
joke: string;
//image?: ;
}

export interface dadJokeActionContent extends Content {
text: string;
}

export interface dadJokeEvalContent extends Content {
text: string;
}

export interface dadJokeEvalResponse {
success: boolean;
response: string;
}

export interface dadJokeProviderResponse {
success: boolean;
data?: dadJokeData;
error?: string;
}
10 changes: 10 additions & 0 deletions packages/plugin-dad-joke/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../core/tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "./src"
},
"include": [
"src"
]
}
20 changes: 20 additions & 0 deletions packages/plugin-dad-joke/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { defineConfig } from "tsup";

export default defineConfig({
entry: ["src/index.ts"],
outDir: "dist",
sourcemap: true,
clean: true,
format: ["esm"], // Ensure you're targeting CommonJS
external: [
"dotenv", // Externalize dotenv to prevent bundling
"fs", // Externalize fs to use Node.js built-in module
"path", // Externalize other built-ins if necessary
"@reflink/reflink",
"@node-llama-cpp",
"https",
"http",
"agentkeepalive",
// Add other modules you want to externalize
],
});
Loading