Skip to content

Commit b6a718b

Browse files
feat: add gravatar tool
1 parent dfd86bc commit b6a718b

File tree

14 files changed

+299
-9
lines changed

14 files changed

+299
-9
lines changed

.github/workflows/main.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
- name: Install pnpm
2424
uses: pnpm/action-setup@v4
2525
with:
26-
version: 10.4.1
26+
version: 10.5.2
2727
run_install: false
2828

2929
- name: Install Node.js

docs/mint.json

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"tools/exa",
6464
"tools/firecrawl",
6565
"tools/hacker-news",
66+
"tools/gravatar",
6667
"tools/hunter",
6768
"tools/jina",
6869
"tools/leadmagic",

docs/tools/gravatar.mdx

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
title: Gravatar
3+
description: Gravatar Profile API.
4+
---
5+
6+
- package: `@agentic/gravatar`
7+
- exports: `class GravatarClient`, `namespace gravatar`
8+
- env vars: `GRAVATAR_API_KEY` _(optional)_
9+
- [source](https://github.com/transitive-bullshit/agentic/blob/main/packages/gravatar/src/gravatar-client.ts)
10+
- [Gravatar API docs](https://docs.gravatar.com/api/profiles/rest-api/)
11+
12+
## Install
13+
14+
<CodeGroup>
15+
```bash npm
16+
npm install @agentic/gravatar
17+
```
18+
19+
```bash yarn
20+
yarn add @agentic/gravatar
21+
```
22+
23+
```bash pnpm
24+
pnpm add @agentic/gravatar
25+
```
26+
27+
</CodeGroup>
28+
29+
## Usage
30+
31+
```ts
32+
import { GravatarClient } from '@agentic/gravatar'
33+
34+
const gravatar = new GravatarClient()
35+
const profile = await gravatar.getProfileByIdentifier('[email protected]')
36+
```

examples/playground/bin/scratch.ts

+9-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import 'dotenv/config'
22

3-
import { ZoomInfoClient } from '@agentic/stdlib'
3+
import * as stdlib from '@agentic/stdlib'
44
import restoreCursor from 'restore-cursor'
55

66
/**
@@ -135,15 +135,17 @@ async function main() {
135135
// category: 'linkedin profile'
136136
// })
137137

138-
const zoomInfo = new ZoomInfoClient()
139-
const res = await zoomInfo.enrichContact({
140-
// emailAddress: '[email protected]'
141-
fullName: 'Kevin Raheja',
142-
companyName: 'HeyGen'
143-
})
138+
// const zoomInfo = new ZoomInfoClient()
139+
// const res = await zoomInfo.enrichContact({
140+
// // emailAddress: '[email protected]'
141+
// fullName: 'Kevin Raheja',
142+
// companyName: 'HeyGen'
143+
// })
144144
// const res = await zoomInfo.searchContacts({
145145
// fullName: 'Kevin Raheja'
146146
// })
147+
const gravatar = new stdlib.GravatarClient()
148+
const res = await gravatar.getProfileByIdentifier('[email protected]')
147149

148150
console.log(JSON.stringify(res, null, 2))
149151
}

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"type": "git",
88
"url": "git+https://github.com/transitive-bullshit/agentic.git"
99
},
10-
"packageManager": "pnpm@10.4.1",
10+
"packageManager": "pnpm@10.5.2",
1111
"engines": {
1212
"node": ">=18"
1313
},

packages/gravatar/package.json

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"name": "@agentic/gravatar",
3+
"version": "7.4.1",
4+
"description": "Agentic SDK for Gravatar.",
5+
"author": "Travis Fischer <[email protected]>",
6+
"license": "MIT",
7+
"repository": {
8+
"type": "git",
9+
"url": "git+https://github.com/transitive-bullshit/agentic.git",
10+
"directory": "packages/gravatar"
11+
},
12+
"type": "module",
13+
"source": "./src/index.ts",
14+
"types": "./dist/index.d.ts",
15+
"sideEffects": false,
16+
"exports": {
17+
".": {
18+
"types": "./dist/index.d.ts",
19+
"import": "./dist/index.js",
20+
"default": "./dist/index.js"
21+
}
22+
},
23+
"files": [
24+
"dist"
25+
],
26+
"scripts": {
27+
"build": "tsup --config ../../tsup.config.ts",
28+
"dev": "tsup --config ../../tsup.config.ts --watch",
29+
"clean": "del dist",
30+
"test": "run-s test:*",
31+
"test:lint": "eslint .",
32+
"test:typecheck": "tsc --noEmit"
33+
},
34+
"dependencies": {
35+
"@agentic/core": "workspace:*",
36+
"ky": "^1.7.5",
37+
"p-throttle": "^6.2.0"
38+
},
39+
"peerDependencies": {
40+
"zod": "^3.24.2"
41+
},
42+
"devDependencies": {
43+
"@agentic/tsconfig": "workspace:*"
44+
},
45+
"publishConfig": {
46+
"access": "public"
47+
}
48+
}

packages/gravatar/readme.md

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<p align="center">
2+
<a href="https://agentic.so">
3+
<img alt="Agentic" src="https://raw.githubusercontent.com/transitive-bullshit/agentic/main/docs/media/agentic-header.jpg" width="308">
4+
</a>
5+
</p>
6+
7+
<p align="center">
8+
<em>AI agent stdlib that works with any LLM and TypeScript AI SDK.</em>
9+
</p>
10+
11+
<p align="center">
12+
<a href="https://github.com/transitive-bullshit/agentic/actions/workflows/main.yml"><img alt="Build Status" src="https://github.com/transitive-bullshit/agentic/actions/workflows/main.yml/badge.svg" /></a>
13+
<a href="https://www.npmjs.com/package/@agentic/stdlib"><img alt="NPM" src="https://img.shields.io/npm/v/@agentic/stdlib.svg" /></a>
14+
<a href="https://github.com/transitive-bullshit/agentic/blob/main/license"><img alt="MIT License" src="https://img.shields.io/badge/license-MIT-blue" /></a>
15+
<a href="https://prettier.io"><img alt="Prettier Code Formatting" src="https://img.shields.io/badge/code_style-prettier-brightgreen.svg" /></a>
16+
</p>
17+
18+
# Agentic
19+
20+
**See the [github repo](https://github.com/transitive-bullshit/agentic) or [docs](https://agentic.so) for more info.**
21+
22+
## License
23+
24+
MIT © [Travis Fischer](https://x.com/transitive_bs)
+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import crypto from 'node:crypto'
2+
3+
import {
4+
aiFunction,
5+
AIFunctionsProvider,
6+
getEnv,
7+
throttleKy
8+
} from '@agentic/core'
9+
import defaultKy, { type KyInstance } from 'ky'
10+
import pThrottle from 'p-throttle'
11+
import z from 'zod'
12+
13+
export namespace gravatar {
14+
export const API_BASE_URL = 'https://api.gravatar.com'
15+
16+
// Allow up to 100 unauthenticated requests per hour by default.
17+
export const unauthenticatedThrottle = pThrottle({
18+
limit: 100,
19+
interval: 60 * 60 * 1000
20+
})
21+
22+
// Allow up to 1000 authenticated requests per hour by default.
23+
export const authenticatedThrottle = pThrottle({
24+
limit: 1000,
25+
interval: 60 * 60 * 1000
26+
})
27+
28+
export type GetProfileByIdentifierOptions = {
29+
email: string
30+
}
31+
32+
export interface Profile {
33+
/** The SHA256 hash of the user’s primary email address. */
34+
hash: string
35+
/** The user’s display name that appears on their profile. */
36+
display_name: string
37+
/** The full URL to the user’s Gravatar profile. */
38+
profile_url: string
39+
/** The URL to the user’s avatar image, if set. */
40+
avatar_url: string
41+
/** Alternative text describing the user’s avatar. */
42+
avatar_alt_text: string
43+
/** The user’s geographical location. */
44+
location: string
45+
/** A short biography or description about the user found on their profile. */
46+
description: string
47+
/** The user’s current job title. */
48+
job_title: string
49+
/** The name of the company where the user is employed. */
50+
company: string
51+
/** 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. */
52+
verified_accounts: any[]
53+
/** A phonetic guide to pronouncing the user’s name. */
54+
pronunciation: string
55+
/** The pronouns the user prefers to use. */
56+
pronouns: string
57+
58+
/** 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. */
59+
number_verified_accounts?: number
60+
61+
/** 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" */
62+
last_profile_edit?: string
63+
64+
/** The date the user registered their account. This property is only provided in authenticated API requests. Example: "2021-10-01" */
65+
registration_date?: string
66+
}
67+
}
68+
69+
/**
70+
* A client for the Gravatar API.
71+
*
72+
* API key is optional.
73+
*
74+
* @see https://docs.gravatar.com/getting-started/
75+
*/
76+
export class GravatarClient extends AIFunctionsProvider {
77+
protected readonly ky: KyInstance
78+
protected readonly apiKey?: string
79+
protected readonly apiBaseUrl: string
80+
81+
constructor({
82+
apiKey = getEnv('GRAVATAR_API_KEY'),
83+
apiBaseUrl = gravatar.API_BASE_URL,
84+
timeoutMs = 60_000,
85+
throttle = true,
86+
ky = defaultKy
87+
}: {
88+
apiKey?: string
89+
apiBaseUrl?: string
90+
timeoutMs?: number
91+
throttle?: boolean
92+
ky?: KyInstance
93+
} = {}) {
94+
super()
95+
96+
// API key is optional
97+
this.apiKey = apiKey
98+
this.apiBaseUrl = apiBaseUrl
99+
100+
const throttledKy = throttle
101+
? throttleKy(
102+
ky,
103+
apiKey
104+
? gravatar.authenticatedThrottle
105+
: gravatar.unauthenticatedThrottle
106+
)
107+
: ky
108+
109+
this.ky = throttledKy.extend({
110+
prefixUrl: apiBaseUrl,
111+
timeout: timeoutMs,
112+
headers: Object.fromEntries(
113+
apiKey ? [['Authorization', `Bearer ${apiKey}`]] : []
114+
)
115+
})
116+
}
117+
118+
@aiFunction({
119+
name: 'gravatar_get_profile',
120+
description:
121+
'Get Gravatar profile by email. Returns a profile object or `undefined` if not found.',
122+
inputSchema: z.object({
123+
email: z.string()
124+
})
125+
})
126+
async getProfileByIdentifier(
127+
emailOrOpts: string | gravatar.GetProfileByIdentifierOptions
128+
): Promise<gravatar.Profile | undefined> {
129+
const { email } =
130+
typeof emailOrOpts === 'string' ? { email: emailOrOpts } : emailOrOpts
131+
const hashedEmail = crypto
132+
.createHash('SHA256')
133+
.update(email.trim().toLowerCase(), 'utf8')
134+
.digest('hex')
135+
136+
try {
137+
return await this.ky
138+
.get(`v3/profiles/${hashedEmail}`)
139+
.json<gravatar.Profile>()
140+
} catch (err: any) {
141+
if (err.response?.status === 404) {
142+
return
143+
}
144+
145+
throw err
146+
}
147+
}
148+
}

packages/gravatar/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './gravatar-client'

packages/gravatar/tsconfig.json

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"extends": "@agentic/tsconfig/base.json",
3+
"include": ["src"],
4+
"exclude": ["node_modules", "dist"]
5+
}

packages/stdlib/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"@agentic/firecrawl": "workspace:*",
4646
"@agentic/genkit": "workspace:*",
4747
"@agentic/github": "workspace:*",
48+
"@agentic/gravatar": "workspace:*",
4849
"@agentic/hacker-news": "workspace:*",
4950
"@agentic/hunter": "workspace:*",
5051
"@agentic/jina": "workspace:*",

packages/stdlib/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export * from '@agentic/e2b'
88
export * from '@agentic/exa'
99
export * from '@agentic/firecrawl'
1010
export * from '@agentic/github'
11+
export * from '@agentic/gravatar'
1112
export * from '@agentic/hacker-news'
1213
export * from '@agentic/hunter'
1314
export * from '@agentic/jina'

pnpm-lock.yaml

+22
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

readme.md

+1
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ Full docs are available at [agentic.so](https://agentic.so).
157157
| [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. |
158158
| [Exa](https://docs.exa.ai) | `@agentic/exa` | [docs](https://agentic.so/tools/exa) | Web search tailored for LLMs. |
159159
| [Firecrawl](https://www.firecrawl.dev) | `@agentic/firecrawl` | [docs](https://agentic.so/tools/firecrawl) | Website scraping and structured data extraction. |
160+
| [Gravatar](https://docs.gravatar.com/api/profiles/rest-api/) | `@agentic/gravatar` | [docs](https://agentic.so/tools/gravatar) | Gravatar profile API. |
160161
| [HackerNews](https://github.com/HackerNews/API) | `@agentic/hacker-news` | [docs](https://agentic.so/tools/hacker-news) | Official HackerNews API. |
161162
| [Hunter](https://hunter.io) | `@agentic/hunter` | [docs](https://agentic.so/tools/hunter) | Email finder, verifier, and enrichment. |
162163
| [Jina](https://jina.ai/reader) | `@agentic/jina` | [docs](https://agentic.so/tools/jina) | URL scraper and web search. |

0 commit comments

Comments
 (0)