From 6287455dcdb49999e4a1e3e112700cca349a236f Mon Sep 17 00:00:00 2001 From: Travis Fischer Date: Wed, 9 Apr 2025 22:44:21 +0700 Subject: [PATCH] feat: add @agentic/typeform Typeform client --- docs/mint.json | 1 + docs/tools/typeform.mdx | 43 +++++ packages/stdlib/package.json | 1 + packages/stdlib/src/index.ts | 1 + packages/twitter/src/twitter-client.ts | 4 +- packages/typeform/package.json | 45 ++++++ packages/typeform/readme.md | 24 +++ packages/typeform/src/index.ts | 1 + packages/typeform/src/typeform-client.ts | 194 +++++++++++++++++++++++ packages/typeform/tsconfig.json | 5 + pnpm-lock.yaml | 18 +++ readme.md | 1 + 12 files changed, 336 insertions(+), 2 deletions(-) create mode 100644 docs/tools/typeform.mdx create mode 100644 packages/typeform/package.json create mode 100644 packages/typeform/readme.md create mode 100644 packages/typeform/src/index.ts create mode 100644 packages/typeform/src/typeform-client.ts create mode 100644 packages/typeform/tsconfig.json diff --git a/docs/mint.json b/docs/mint.json index 4e3fb80..f529000 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -93,6 +93,7 @@ "tools/tavily", "tools/twilio", "tools/twitter", + "tools/typeform", "tools/weather", "tools/wikidata", "tools/wikipedia", diff --git a/docs/tools/typeform.mdx b/docs/tools/typeform.mdx new file mode 100644 index 0000000..2369967 --- /dev/null +++ b/docs/tools/typeform.mdx @@ -0,0 +1,43 @@ +--- +title: Typeform +description: Readonly Typeform API client for fetching form insights and responses. +--- + +- package: `@agentic/typeform` +- exports: `class TypeformClient`, `namespace typeform` +- env vars: `TYPEFORM_API_KEY` +- [source](https://github.com/transitive-bullshit/agentic/blob/main/packages/typeform/src/typeform-client.ts) +- [typeform api docs](https://www.typeform.com/developers/get-started/) + +## Install + + +```bash npm +npm install @agentic/typeform +``` + +```bash yarn +yarn add @agentic/typeform +``` + +```bash pnpm +pnpm add @agentic/typeform +``` + + + +## Usage + +```ts +import { TypeformClient } from '@agentic/typeform' + +const typeform = new TypeformClient() + +const responses = await typeform.getResponsesForForm({ + formId: 'TODO' +}) + +const insights = await typeform.getInsightsForForm({ + formId: 'TODO' +}) +``` diff --git a/packages/stdlib/package.json b/packages/stdlib/package.json index 4fd4128..80b333b 100644 --- a/packages/stdlib/package.json +++ b/packages/stdlib/package.json @@ -72,6 +72,7 @@ "@agentic/tavily": "workspace:*", "@agentic/twilio": "workspace:*", "@agentic/twitter": "workspace:*", + "@agentic/typeform": "workspace:*", "@agentic/weather": "workspace:*", "@agentic/wikidata": "workspace:*", "@agentic/wikipedia": "workspace:*", diff --git a/packages/stdlib/src/index.ts b/packages/stdlib/src/index.ts index 45d06ab..fc7a6cb 100644 --- a/packages/stdlib/src/index.ts +++ b/packages/stdlib/src/index.ts @@ -37,6 +37,7 @@ export * from '@agentic/social-data' export * from '@agentic/tavily' export * from '@agentic/twilio' export * from '@agentic/twitter' +export * from '@agentic/typeform' export * from '@agentic/weather' export * from '@agentic/wikidata' export * from '@agentic/wikipedia' diff --git a/packages/twitter/src/twitter-client.ts b/packages/twitter/src/twitter-client.ts index 726ed51..040ceb0 100644 --- a/packages/twitter/src/twitter-client.ts +++ b/packages/twitter/src/twitter-client.ts @@ -141,10 +141,10 @@ const twitterApiRateLimitsByPlan: Record< } /** - * Twitter API v2 client wrapper with rate-limited methods and `@aiFunction` + * Twitter/X API v2 client wrapper with rate-limited methods and `@aiFunction` * compatibility. * - * Rate limits differ by plan, so make sure theh `twitterApiPlan` parameter is + * Rate limits differ by plan, so make sure the `twitterApiPlan` parameter is * properly set to maximize your rate-limit usage. * * @note This class does not handle distributed rate-limits. It assumes a diff --git a/packages/typeform/package.json b/packages/typeform/package.json new file mode 100644 index 0000000..af0c52c --- /dev/null +++ b/packages/typeform/package.json @@ -0,0 +1,45 @@ +{ + "name": "@agentic/typeform", + "version": "7.6.3", + "description": "Agentic SDK for the Typeform data API.", + "author": "Travis Fischer ", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/transitive-bullshit/agentic.git", + "directory": "packages/typeform" + }, + "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", + "dev": "tsup --watch", + "clean": "del dist", + "test": "run-s test:*", + "test:lint": "eslint .", + "test:typecheck": "tsc --noEmit" + }, + "dependencies": { + "@agentic/core": "workspace:*", + "ky": "catalog:", + "p-throttle": "catalog:" + }, + "peerDependencies": { + "zod": "catalog:" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/typeform/readme.md b/packages/typeform/readme.md new file mode 100644 index 0000000..38781f3 --- /dev/null +++ b/packages/typeform/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/packages/typeform/src/index.ts b/packages/typeform/src/index.ts new file mode 100644 index 0000000..13caf7d --- /dev/null +++ b/packages/typeform/src/index.ts @@ -0,0 +1 @@ +export * from './typeform-client' diff --git a/packages/typeform/src/typeform-client.ts b/packages/typeform/src/typeform-client.ts new file mode 100644 index 0000000..0cb0f2f --- /dev/null +++ b/packages/typeform/src/typeform-client.ts @@ -0,0 +1,194 @@ +import { + aiFunction, + AIFunctionsProvider, + assert, + getEnv, + sanitizeSearchParams +} from '@agentic/core' +import defaultKy, { type KyInstance } from 'ky' +import { z } from 'zod' + +export namespace typeform { + export const API_BASE_URL = 'https://api.typeform.com' + + export interface GetInsightsForFormResponse { + fields: Array<{ + dropoffs: number + id: string + label: string + ref: string + title: string + type: string + views: number + }> + form: { + platforms: Array<{ + average_time: number + completion_rate: number + platform: string + responses_count: number + total_visits: number + unique_visits: number + }> + summary: { + average_time: number + completion_rate: number + responses_count: number + total_visits: number + unique_visits: number + } + } + } + + export interface GetResponsesForFormParams { + formId: string + pageSize?: number + since?: string + until?: string + completed?: boolean + } + + export interface GetResponsesForFormResponse { + total_items: number + page_count: number + items: Array<{ + landing_id: string + token: string + landed_at: string + submitted_at: string + metadata: { + user_agent: string + platform: string + referer: string + network_id: string + browser: string + } + answers: Array<{ + field: { + id: string + type: string + ref: string + } + type: string + [key: string]: any + }> + hidden: Record + calculated: { + score: number + } + variables: Array<{ + key: string + type: string + [key: string]: any + }> + }> + } +} + +/** + * Readonly Typeform API client for fetching form insights and responses. + * + * @see https://www.typeform.com/developers/get-started/ + */ +export class TypeformClient extends AIFunctionsProvider { + protected readonly ky: KyInstance + protected readonly apiKey: string + protected readonly apiBaseUrl: string + + constructor({ + apiKey = getEnv('TYPEFORM_API_KEY'), + apiBaseUrl = typeform.API_BASE_URL, + ky = defaultKy + }: { + /** Typeform Personal Access Token */ + apiKey?: string + apiBaseUrl?: string + ky?: KyInstance + } = {}) { + assert( + apiKey, + 'TypeformClient missing required "apiKey" (defaults to "TYPEFORM_API_KEY")' + ) + super() + + this.apiKey = apiKey + this.apiBaseUrl = apiBaseUrl + + this.ky = ky.extend({ + prefixUrl: this.apiBaseUrl, + headers: { + Authorization: `Bearer ${this.apiKey}` + } + }) + } + + /** + * Retrieves insights and analytics for a Typeform form. + */ + @aiFunction({ + name: 'typeform_get_insights_for_form', + description: 'Retrieve insights and analytics for a Typeform form.', + inputSchema: z.object({ + formId: z + .string() + .describe('The ID of the Typeform form to get insights for.') + }) + }) + async getInsightsForForm( + formIdOrOptions: string | { formId: string } + ): Promise { + const { formId } = + typeof formIdOrOptions === 'string' + ? { formId: formIdOrOptions } + : formIdOrOptions + + const encodedFormId = encodeURIComponent(formId) + return this.ky + .get(`insights/${encodedFormId}/summary`) + .json() + } + + /** + * Retrieves responses for a Typeform form. + */ + @aiFunction({ + name: 'typeform_get_responses_for_form', + description: 'Retrieve responses for a Typeform form.', + inputSchema: z.object({ + formId: z + .string() + .describe('The ID of the Typeform form to get responses for.'), + pageSize: z + .number() + .describe('The number of responses to retrieve per page.') + .optional(), + since: z + .string() + .describe('The date to start retrieving responses from.') + .optional(), + until: z + .string() + .describe('The date to stop retrieving responses at.') + .optional(), + completed: z + .boolean() + .describe('Filter responses by completion status.') + .optional() + }) + }) + async getResponsesForForm( + formIdOrOptions: string | typeform.GetResponsesForFormParams + ): Promise { + const { formId, ...params } = + typeof formIdOrOptions === 'string' + ? { formId: formIdOrOptions } + : formIdOrOptions + + const encodedFormId = encodeURIComponent(formId) + return this.ky + .get(`forms/${encodedFormId}/responses`, { + searchParams: sanitizeSearchParams(params) + }) + .json() + } +} diff --git a/packages/typeform/tsconfig.json b/packages/typeform/tsconfig.json new file mode 100644 index 0000000..51348fa --- /dev/null +++ b/packages/typeform/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "@fisch0920/config/tsconfig-node", + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 027a3e6..2139c12 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1191,6 +1191,9 @@ importers: '@agentic/twitter': specifier: workspace:* version: link:../twitter + '@agentic/typeform': + specifier: workspace:* + version: link:../typeform '@agentic/weather': specifier: workspace:* version: link:../weather @@ -1267,6 +1270,21 @@ importers: specifier: 'catalog:' version: 3.24.2 + packages/typeform: + dependencies: + '@agentic/core': + specifier: workspace:* + version: link:../core + ky: + specifier: 'catalog:' + version: 1.8.0 + p-throttle: + specifier: 'catalog:' + version: 6.2.0 + zod: + specifier: 'catalog:' + version: 3.24.2 + packages/weather: dependencies: '@agentic/core': diff --git a/readme.md b/readme.md index a9dde29..5c1308c 100644 --- a/readme.md +++ b/readme.md @@ -218,6 +218,7 @@ Full docs are available at [agentic.so](https://agentic.so). | [Tavily](https://tavily.com) | `@agentic/tavily` | [docs](https://agentic.so/tools/tavily) | Web search API tailored for LLMs. | | [Twilio](https://www.twilio.com/docs/conversations/api) | `@agentic/twilio` | [docs](https://agentic.so/tools/twilio) | Twilio conversation API to send and receive SMS messages. | | [Twitter](https://developer.x.com/en/docs/twitter-api) | `@agentic/twitter` | [docs](https://agentic.so/tools/twitter) | Basic Twitter API methods for fetching users, tweets, and searching recent tweets. Includes support for plan-aware rate-limiting. Uses [Nango](https://www.nango.dev) for OAuth support. | +| [Typeform](https://www.typeform.com/developers/get-started/) | `@agentic/typeform` | [docs](https://agentic.so/tools/typeform) | Readonly Typeform client for fetching form insights and responses. | | [Weather](https://www.weatherapi.com) | `@agentic/weather` | [docs](https://agentic.so/tools/weather) | Basic access to current weather data based on location. | | [Wikidata](https://www.wikidata.org/wiki/Wikidata:Data_access) | `@agentic/wikidata` | [docs](https://agentic.so/tools/wikidata) | Basic Wikidata client. | | [Wikipedia](https://www.mediawiki.org/wiki/API) | `@agentic/wikipedia` | [docs](https://agentic.so/tools/wikipedia) | Wikipedia page search and summaries. |