From 42a7241bcdee5a0126371deeb6709a6071ee4759 Mon Sep 17 00:00:00 2001 From: Travis Fischer Date: Fri, 28 Feb 2025 05:33:25 +0700 Subject: [PATCH] feat: add gravatar tool --- legacy/.github/workflows/main.yml | 2 +- legacy/docs/mint.json | 1 + legacy/docs/tools/gravatar.mdx | 36 +++++ legacy/examples/playground/bin/scratch.ts | 16 +- legacy/package.json | 2 +- legacy/packages/gravatar/package.json | 48 ++++++ legacy/packages/gravatar/readme.md | 24 +++ .../packages/gravatar/src/gravatar-client.ts | 148 ++++++++++++++++++ legacy/packages/gravatar/src/index.ts | 1 + legacy/packages/gravatar/tsconfig.json | 5 + legacy/packages/stdlib/package.json | 1 + legacy/packages/stdlib/src/index.ts | 1 + legacy/pnpm-lock.yaml | 22 +++ legacy/readme.md | 1 + 14 files changed, 299 insertions(+), 9 deletions(-) create mode 100644 legacy/docs/tools/gravatar.mdx create mode 100644 legacy/packages/gravatar/package.json create mode 100644 legacy/packages/gravatar/readme.md create mode 100644 legacy/packages/gravatar/src/gravatar-client.ts create mode 100644 legacy/packages/gravatar/src/index.ts create mode 100644 legacy/packages/gravatar/tsconfig.json diff --git a/legacy/.github/workflows/main.yml b/legacy/.github/workflows/main.yml index cd295214..3dbae880 100644 --- a/legacy/.github/workflows/main.yml +++ b/legacy/.github/workflows/main.yml @@ -23,7 +23,7 @@ jobs: - name: Install pnpm uses: pnpm/action-setup@v4 with: - version: 10.4.1 + version: 10.5.2 run_install: false - name: Install Node.js diff --git a/legacy/docs/mint.json b/legacy/docs/mint.json index e484038a..e8ed2ca8 100644 --- a/legacy/docs/mint.json +++ b/legacy/docs/mint.json @@ -63,6 +63,7 @@ "tools/exa", "tools/firecrawl", "tools/hacker-news", + "tools/gravatar", "tools/hunter", "tools/jina", "tools/leadmagic", diff --git a/legacy/docs/tools/gravatar.mdx b/legacy/docs/tools/gravatar.mdx new file mode 100644 index 00000000..9d6fba2f --- /dev/null +++ b/legacy/docs/tools/gravatar.mdx @@ -0,0 +1,36 @@ +--- +title: Gravatar +description: Gravatar Profile API. +--- + +- package: `@agentic/gravatar` +- exports: `class GravatarClient`, `namespace gravatar` +- env vars: `GRAVATAR_API_KEY` _(optional)_ +- [source](https://github.com/transitive-bullshit/agentic/blob/main/packages/gravatar/src/gravatar-client.ts) +- [Gravatar API docs](https://docs.gravatar.com/api/profiles/rest-api/) + +## Install + + +```bash npm +npm install @agentic/gravatar +``` + +```bash yarn +yarn add @agentic/gravatar +``` + +```bash pnpm +pnpm add @agentic/gravatar +``` + + + +## Usage + +```ts +import { GravatarClient } from '@agentic/gravatar' + +const gravatar = new GravatarClient() +const profile = await gravatar.getProfileByIdentifier('my-email@example.com') +``` diff --git a/legacy/examples/playground/bin/scratch.ts b/legacy/examples/playground/bin/scratch.ts index 3fe6d360..4aafe4b1 100644 --- a/legacy/examples/playground/bin/scratch.ts +++ b/legacy/examples/playground/bin/scratch.ts @@ -1,6 +1,6 @@ import 'dotenv/config' -import { ZoomInfoClient } from '@agentic/stdlib' +import * as stdlib from '@agentic/stdlib' import restoreCursor from 'restore-cursor' /** @@ -135,15 +135,17 @@ async function main() { // category: 'linkedin profile' // }) - const zoomInfo = new ZoomInfoClient() - const res = await zoomInfo.enrichContact({ - // emailAddress: 'travis@transitivebullsh.it' - fullName: 'Kevin Raheja', - companyName: 'HeyGen' - }) + // const zoomInfo = new ZoomInfoClient() + // const res = await zoomInfo.enrichContact({ + // // emailAddress: 'travis@transitivebullsh.it' + // fullName: 'Kevin Raheja', + // companyName: 'HeyGen' + // }) // const res = await zoomInfo.searchContacts({ // fullName: 'Kevin Raheja' // }) + const gravatar = new stdlib.GravatarClient() + const res = await gravatar.getProfileByIdentifier('email@example.com') console.log(JSON.stringify(res, null, 2)) } diff --git a/legacy/package.json b/legacy/package.json index 00a27369..509e74b8 100644 --- a/legacy/package.json +++ b/legacy/package.json @@ -7,7 +7,7 @@ "type": "git", "url": "git+https://github.com/transitive-bullshit/agentic.git" }, - "packageManager": "pnpm@10.4.1", + "packageManager": "pnpm@10.5.2", "engines": { "node": ">=18" }, diff --git a/legacy/packages/gravatar/package.json b/legacy/packages/gravatar/package.json new file mode 100644 index 00000000..0e286a16 --- /dev/null +++ b/legacy/packages/gravatar/package.json @@ -0,0 +1,48 @@ +{ + "name": "@agentic/gravatar", + "version": "7.4.1", + "description": "Agentic SDK for Gravatar.", + "author": "Travis Fischer ", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/transitive-bullshit/agentic.git", + "directory": "packages/gravatar" + }, + "type": "module", + "source": "./src/index.ts", + "types": "./dist/index.d.ts", + "sideEffects": false, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsup --config ../../tsup.config.ts", + "dev": "tsup --config ../../tsup.config.ts --watch", + "clean": "del dist", + "test": "run-s test:*", + "test:lint": "eslint .", + "test:typecheck": "tsc --noEmit" + }, + "dependencies": { + "@agentic/core": "workspace:*", + "ky": "^1.7.5", + "p-throttle": "^6.2.0" + }, + "peerDependencies": { + "zod": "^3.24.2" + }, + "devDependencies": { + "@agentic/tsconfig": "workspace:*" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/legacy/packages/gravatar/readme.md b/legacy/packages/gravatar/readme.md new file mode 100644 index 00000000..38781f32 --- /dev/null +++ b/legacy/packages/gravatar/readme.md @@ -0,0 +1,24 @@ +

+ + Agentic + +

+ +

+ AI agent stdlib that works with any LLM and TypeScript AI SDK. +

+ +

+ Build Status + NPM + MIT License + Prettier Code Formatting +

+ +# Agentic + +**See the [github repo](https://github.com/transitive-bullshit/agentic) or [docs](https://agentic.so) for more info.** + +## License + +MIT © [Travis Fischer](https://x.com/transitive_bs) diff --git a/legacy/packages/gravatar/src/gravatar-client.ts b/legacy/packages/gravatar/src/gravatar-client.ts new file mode 100644 index 00000000..6f51fd9a --- /dev/null +++ b/legacy/packages/gravatar/src/gravatar-client.ts @@ -0,0 +1,148 @@ +import crypto from 'node:crypto' + +import { + aiFunction, + AIFunctionsProvider, + getEnv, + throttleKy +} from '@agentic/core' +import defaultKy, { type KyInstance } from 'ky' +import pThrottle from 'p-throttle' +import z from 'zod' + +export namespace gravatar { + export const API_BASE_URL = 'https://api.gravatar.com' + + // Allow up to 100 unauthenticated requests per hour by default. + export const unauthenticatedThrottle = pThrottle({ + limit: 100, + interval: 60 * 60 * 1000 + }) + + // Allow up to 1000 authenticated requests per hour by default. + export const authenticatedThrottle = pThrottle({ + limit: 1000, + interval: 60 * 60 * 1000 + }) + + export type GetProfileByIdentifierOptions = { + email: string + } + + export interface Profile { + /** The SHA256 hash of the user’s primary email address. */ + hash: string + /** The user’s display name that appears on their profile. */ + display_name: string + /** The full URL to the user’s Gravatar profile. */ + profile_url: string + /** The URL to the user’s avatar image, if set. */ + avatar_url: string + /** Alternative text describing the user’s avatar. */ + avatar_alt_text: string + /** The user’s geographical location. */ + location: string + /** A short biography or description about the user found on their profile. */ + description: string + /** The user’s current job title. */ + job_title: string + /** The name of the company where the user is employed. */ + company: string + /** An array of verified accounts the user has added to their profile. The number of verified accounts displayed is limited to a maximum of 4 in unauthenticated requests. */ + verified_accounts: any[] + /** A phonetic guide to pronouncing the user’s name. */ + pronunciation: string + /** The pronouns the user prefers to use. */ + pronouns: string + + /** The total number of verified accounts the user has added to their profile, including those not displayed on their profile. This property is only provided in authenticated API requests. */ + number_verified_accounts?: number + + /** The date and time (UTC) when the user last edited their profile. This property is only provided in authenticated API requests. Example: "2021-10-01T12:00:00Z" */ + last_profile_edit?: string + + /** The date the user registered their account. This property is only provided in authenticated API requests. Example: "2021-10-01" */ + registration_date?: string + } +} + +/** + * A client for the Gravatar API. + * + * API key is optional. + * + * @see https://docs.gravatar.com/getting-started/ + */ +export class GravatarClient extends AIFunctionsProvider { + protected readonly ky: KyInstance + protected readonly apiKey?: string + protected readonly apiBaseUrl: string + + constructor({ + apiKey = getEnv('GRAVATAR_API_KEY'), + apiBaseUrl = gravatar.API_BASE_URL, + timeoutMs = 60_000, + throttle = true, + ky = defaultKy + }: { + apiKey?: string + apiBaseUrl?: string + timeoutMs?: number + throttle?: boolean + ky?: KyInstance + } = {}) { + super() + + // API key is optional + this.apiKey = apiKey + this.apiBaseUrl = apiBaseUrl + + const throttledKy = throttle + ? throttleKy( + ky, + apiKey + ? gravatar.authenticatedThrottle + : gravatar.unauthenticatedThrottle + ) + : ky + + this.ky = throttledKy.extend({ + prefixUrl: apiBaseUrl, + timeout: timeoutMs, + headers: Object.fromEntries( + apiKey ? [['Authorization', `Bearer ${apiKey}`]] : [] + ) + }) + } + + @aiFunction({ + name: 'gravatar_get_profile', + description: + 'Get Gravatar profile by email. Returns a profile object or `undefined` if not found.', + inputSchema: z.object({ + email: z.string() + }) + }) + async getProfileByIdentifier( + emailOrOpts: string | gravatar.GetProfileByIdentifierOptions + ): Promise { + const { email } = + typeof emailOrOpts === 'string' ? { email: emailOrOpts } : emailOrOpts + const hashedEmail = crypto + .createHash('SHA256') + .update(email.trim().toLowerCase(), 'utf8') + .digest('hex') + + try { + return await this.ky + .get(`v3/profiles/${hashedEmail}`) + .json() + } catch (err: any) { + if (err.response?.status === 404) { + return + } + + throw err + } + } +} diff --git a/legacy/packages/gravatar/src/index.ts b/legacy/packages/gravatar/src/index.ts new file mode 100644 index 00000000..80bdc7fc --- /dev/null +++ b/legacy/packages/gravatar/src/index.ts @@ -0,0 +1 @@ +export * from './gravatar-client' diff --git a/legacy/packages/gravatar/tsconfig.json b/legacy/packages/gravatar/tsconfig.json new file mode 100644 index 00000000..6c8d720c --- /dev/null +++ b/legacy/packages/gravatar/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "@agentic/tsconfig/base.json", + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/legacy/packages/stdlib/package.json b/legacy/packages/stdlib/package.json index 46e970f2..a12424bf 100644 --- a/legacy/packages/stdlib/package.json +++ b/legacy/packages/stdlib/package.json @@ -45,6 +45,7 @@ "@agentic/firecrawl": "workspace:*", "@agentic/genkit": "workspace:*", "@agentic/github": "workspace:*", + "@agentic/gravatar": "workspace:*", "@agentic/hacker-news": "workspace:*", "@agentic/hunter": "workspace:*", "@agentic/jina": "workspace:*", diff --git a/legacy/packages/stdlib/src/index.ts b/legacy/packages/stdlib/src/index.ts index 9fabd3aa..c099adb3 100644 --- a/legacy/packages/stdlib/src/index.ts +++ b/legacy/packages/stdlib/src/index.ts @@ -8,6 +8,7 @@ export * from '@agentic/e2b' export * from '@agentic/exa' export * from '@agentic/firecrawl' export * from '@agentic/github' +export * from '@agentic/gravatar' export * from '@agentic/hacker-news' export * from '@agentic/hunter' export * from '@agentic/jina' diff --git a/legacy/pnpm-lock.yaml b/legacy/pnpm-lock.yaml index be276c97..13b6f935 100644 --- a/legacy/pnpm-lock.yaml +++ b/legacy/pnpm-lock.yaml @@ -495,6 +495,25 @@ importers: specifier: workspace:* version: link:../tsconfig + packages/gravatar: + dependencies: + '@agentic/core': + specifier: workspace:* + version: link:../core + ky: + specifier: ^1.7.5 + version: 1.7.5 + p-throttle: + specifier: ^6.2.0 + version: 6.2.0 + zod: + specifier: ^3.24.2 + version: 3.24.2 + devDependencies: + '@agentic/tsconfig': + specifier: workspace:* + version: link:../tsconfig + packages/hacker-news: dependencies: '@agentic/core': @@ -883,6 +902,9 @@ importers: '@agentic/github': specifier: workspace:* version: link:../github + '@agentic/gravatar': + specifier: workspace:* + version: link:../gravatar '@agentic/hacker-news': specifier: workspace:* version: link:../hacker-news diff --git a/legacy/readme.md b/legacy/readme.md index 4dc35526..9fb16c56 100644 --- a/legacy/readme.md +++ b/legacy/readme.md @@ -157,6 +157,7 @@ Full docs are available at [agentic.so](https://agentic.so). | [E2B](https://e2b.dev) | `@agentic/e2b` | [docs](https://agentic.so/tools/e2b) | Hosted Python code interpreter sandbox which is really useful for data analysis, flexible code execution, and advanced reasoning on-the-fly. | | [Exa](https://docs.exa.ai) | `@agentic/exa` | [docs](https://agentic.so/tools/exa) | Web search tailored for LLMs. | | [Firecrawl](https://www.firecrawl.dev) | `@agentic/firecrawl` | [docs](https://agentic.so/tools/firecrawl) | Website scraping and structured data extraction. | +| [Gravatar](https://docs.gravatar.com/api/profiles/rest-api/) | `@agentic/gravatar` | [docs](https://agentic.so/tools/gravatar) | Gravatar profile API. | | [HackerNews](https://github.com/HackerNews/API) | `@agentic/hacker-news` | [docs](https://agentic.so/tools/hacker-news) | Official HackerNews API. | | [Hunter](https://hunter.io) | `@agentic/hunter` | [docs](https://agentic.so/tools/hunter) | Email finder, verifier, and enrichment. | | [Jina](https://jina.ai/reader) | `@agentic/jina` | [docs](https://agentic.so/tools/jina) | URL scraper and web search. |