diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx index b79a166e901..6f53db86f8b 100644 --- a/apps/docs/components/icons.tsx +++ b/apps/docs/components/icons.tsx @@ -1285,6 +1285,17 @@ export function StartIcon(props: SVGProps) { ) } +export function ProfoundIcon(props: SVGProps) { + return ( + + + + ) +} + export function PineconeIcon(props: SVGProps) { return ( = { polymarket: PolymarketIcon, postgresql: PostgresIcon, posthog: PosthogIcon, + profound: ProfoundIcon, pulse_v2: PulseIcon, qdrant: QdrantIcon, quiver: QuiverIcon, diff --git a/apps/docs/content/docs/en/tools/meta.json b/apps/docs/content/docs/en/tools/meta.json index a52a54cf0f8..49ee064ffb1 100644 --- a/apps/docs/content/docs/en/tools/meta.json +++ b/apps/docs/content/docs/en/tools/meta.json @@ -121,6 +121,7 @@ "polymarket", "postgresql", "posthog", + "profound", "pulse", "qdrant", "quiver", diff --git a/apps/docs/content/docs/en/tools/profound.mdx b/apps/docs/content/docs/en/tools/profound.mdx new file mode 100644 index 00000000000..8f2cb0e83cd --- /dev/null +++ b/apps/docs/content/docs/en/tools/profound.mdx @@ -0,0 +1,626 @@ +--- +title: Profound +description: AI visibility and analytics with Profound +--- + +import { BlockInfoCard } from "@/components/ui/block-info-card" + + + +{/* MANUAL-CONTENT-START:intro */} +[Profound](https://tryprofound.com/) is an AI visibility and analytics platform that helps brands understand how they appear across AI-powered search engines, chatbots, and assistants. It tracks mentions, citations, sentiment, bot traffic, and referral patterns across platforms like ChatGPT, Perplexity, Google AI Overviews, and more. + +With the Profound integration in Sim, you can: + +- **Monitor AI Visibility**: Track share of voice, visibility scores, and mention counts across AI platforms for your brand and competitors. +- **Analyze Sentiment**: Measure how positively or negatively your brand is discussed in AI-generated responses. +- **Track Citations**: See which URLs are being cited by AI models and your citation share relative to competitors. +- **Monitor Bot Traffic**: Analyze AI crawler activity on your domain, including GPTBot, ClaudeBot, and other AI agents, with hourly granularity. +- **Track Referral Traffic**: Monitor human visits arriving from AI platforms to your website. +- **Explore Prompt Data**: Access raw prompt-answer pairs, query fanouts, and prompt volume trends across AI platforms. +- **Optimize Content**: Get AEO (Answer Engine Optimization) scores and actionable recommendations to improve how AI models reference your content. +- **Manage Categories & Assets**: List and explore your tracked categories, assets (brands), topics, tags, personas, and regions. + +These tools let your agents automate AI visibility monitoring, competitive intelligence, and content optimization workflows. To use the Profound integration, you'll need a Profound account with API access. +{/* MANUAL-CONTENT-END */} + + +## Usage Instructions + +Track how your brand appears across AI platforms. Monitor visibility scores, sentiment, citations, bot traffic, referrals, content optimization, and prompt volumes with Profound. + + + +## Tools + +### `profound_list_categories` + +List all organization categories in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `categories` | json | List of organization categories | +| ↳ `id` | string | Category ID | +| ↳ `name` | string | Category name | + +### `profound_list_regions` + +List all organization regions in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `regions` | json | List of organization regions | +| ↳ `id` | string | Region ID \(UUID\) | +| ↳ `name` | string | Region name | + +### `profound_list_models` + +List all AI models/platforms tracked in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `models` | json | List of AI models/platforms | +| ↳ `id` | string | Model ID \(UUID\) | +| ↳ `name` | string | Model/platform name | + +### `profound_list_domains` + +List all organization domains in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `domains` | json | List of organization domains | +| ↳ `id` | string | Domain ID \(UUID\) | +| ↳ `name` | string | Domain name | +| ↳ `createdAt` | string | When the domain was added | + +### `profound_list_assets` + +List all organization assets (companies/brands) across all categories in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `assets` | json | List of organization assets with category info | +| ↳ `id` | string | Asset ID | +| ↳ `name` | string | Asset/company name | +| ↳ `website` | string | Asset website URL | +| ↳ `alternateDomains` | json | Alternate domain names | +| ↳ `isOwned` | boolean | Whether this asset is owned by the organization | +| ↳ `createdAt` | string | When the asset was created | +| ↳ `logoUrl` | string | URL of the asset logo | +| ↳ `categoryId` | string | Category ID the asset belongs to | +| ↳ `categoryName` | string | Category name | + +### `profound_list_personas` + +List all organization personas across all categories in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `personas` | json | List of organization personas with profile details | +| ↳ `id` | string | Persona ID | +| ↳ `name` | string | Persona name | +| ↳ `categoryId` | string | Category ID | +| ↳ `categoryName` | string | Category name | +| ↳ `persona` | json | Persona profile with behavior, employment, and demographics | + +### `profound_category_topics` + +List topics for a specific category in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | +| `categoryId` | string | Yes | Category ID \(UUID\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `topics` | json | List of topics in the category | +| ↳ `id` | string | Topic ID \(UUID\) | +| ↳ `name` | string | Topic name | + +### `profound_category_tags` + +List tags for a specific category in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | +| `categoryId` | string | Yes | Category ID \(UUID\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `tags` | json | List of tags in the category | +| ↳ `id` | string | Tag ID \(UUID\) | +| ↳ `name` | string | Tag name | + +### `profound_category_prompts` + +List prompts for a specific category in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | +| `categoryId` | string | Yes | Category ID \(UUID\) | +| `limit` | number | No | Maximum number of results \(default 10000, max 10000\) | +| `cursor` | string | No | Pagination cursor from previous response | +| `orderDir` | string | No | Sort direction: asc or desc \(default desc\) | +| `promptType` | string | No | Comma-separated prompt types to filter: visibility, sentiment | +| `topicId` | string | No | Comma-separated topic IDs \(UUIDs\) to filter by | +| `tagId` | string | No | Comma-separated tag IDs \(UUIDs\) to filter by | +| `regionId` | string | No | Comma-separated region IDs \(UUIDs\) to filter by | +| `platformId` | string | No | Comma-separated platform IDs \(UUIDs\) to filter by | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `totalRows` | number | Total number of prompts | +| `nextCursor` | string | Cursor for next page of results | +| `prompts` | json | List of prompts | +| ↳ `id` | string | Prompt ID | +| ↳ `prompt` | string | Prompt text | +| ↳ `promptType` | string | Prompt type \(visibility or sentiment\) | +| ↳ `topicId` | string | Topic ID | +| ↳ `topicName` | string | Topic name | +| ↳ `tags` | json | Associated tags | +| ↳ `regions` | json | Associated regions | +| ↳ `platforms` | json | Associated platforms | +| ↳ `createdAt` | string | When the prompt was created | + +### `profound_category_assets` + +List assets (companies/brands) for a specific category in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | +| `categoryId` | string | Yes | Category ID \(UUID\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `assets` | json | List of assets in the category | +| ↳ `id` | string | Asset ID | +| ↳ `name` | string | Asset/company name | +| ↳ `website` | string | Website URL | +| ↳ `alternateDomains` | json | Alternate domain names | +| ↳ `isOwned` | boolean | Whether the asset is owned by the organization | +| ↳ `createdAt` | string | When the asset was created | +| ↳ `logoUrl` | string | URL of the asset logo | + +### `profound_category_personas` + +List personas for a specific category in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | +| `categoryId` | string | Yes | Category ID \(UUID\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `personas` | json | List of personas in the category | +| ↳ `id` | string | Persona ID | +| ↳ `name` | string | Persona name | +| ↳ `persona` | json | Persona profile with behavior, employment, and demographics | + +### `profound_visibility_report` + +Query AI visibility report for a category in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | +| `categoryId` | string | Yes | Category ID \(UUID\) | +| `startDate` | string | Yes | Start date \(YYYY-MM-DD or ISO 8601\) | +| `endDate` | string | Yes | End date \(YYYY-MM-DD or ISO 8601\) | +| `metrics` | string | Yes | Comma-separated metrics: share_of_voice, mentions_count, visibility_score, executions, average_position | +| `dimensions` | string | No | Comma-separated dimensions: date, region, topic, model, asset_name, prompt, tag, persona | +| `dateInterval` | string | No | Date interval: hour, day, week, month, year | +| `filters` | string | No | JSON array of filter objects, e.g. \[\{"field":"asset_name","operator":"is","value":"Company"\}\] | +| `limit` | number | No | Maximum number of results \(default 10000, max 50000\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `totalRows` | number | Total number of rows in the report | +| `data` | json | Report data rows with metrics and dimension values | +| ↳ `metrics` | json | Array of metric values matching requested metrics order | +| ↳ `dimensions` | json | Array of dimension values matching requested dimensions order | + +### `profound_sentiment_report` + +Query sentiment report for a category in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | +| `categoryId` | string | Yes | Category ID \(UUID\) | +| `startDate` | string | Yes | Start date \(YYYY-MM-DD or ISO 8601\) | +| `endDate` | string | Yes | End date \(YYYY-MM-DD or ISO 8601\) | +| `metrics` | string | Yes | Comma-separated metrics: positive, negative, occurrences | +| `dimensions` | string | No | Comma-separated dimensions: theme, date, region, topic, model, asset_name, tag, prompt, sentiment_type, persona | +| `dateInterval` | string | No | Date interval: hour, day, week, month, year | +| `filters` | string | No | JSON array of filter objects, e.g. \[\{"field":"asset_name","operator":"is","value":"Company"\}\] | +| `limit` | number | No | Maximum number of results \(default 10000, max 50000\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `totalRows` | number | Total number of rows in the report | +| `data` | json | Report data rows with metrics and dimension values | +| ↳ `metrics` | json | Array of metric values matching requested metrics order | +| ↳ `dimensions` | json | Array of dimension values matching requested dimensions order | + +### `profound_citations_report` + +Query citations report for a category in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | +| `categoryId` | string | Yes | Category ID \(UUID\) | +| `startDate` | string | Yes | Start date \(YYYY-MM-DD or ISO 8601\) | +| `endDate` | string | Yes | End date \(YYYY-MM-DD or ISO 8601\) | +| `metrics` | string | Yes | Comma-separated metrics: count, citation_share | +| `dimensions` | string | No | Comma-separated dimensions: hostname, path, date, region, topic, model, tag, prompt, url, root_domain, persona, citation_category | +| `dateInterval` | string | No | Date interval: hour, day, week, month, year | +| `filters` | string | No | JSON array of filter objects, e.g. \[\{"field":"hostname","operator":"is","value":"example.com"\}\] | +| `limit` | number | No | Maximum number of results \(default 10000, max 50000\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `totalRows` | number | Total number of rows in the report | +| `data` | json | Report data rows with metrics and dimension values | +| ↳ `metrics` | json | Array of metric values matching requested metrics order | +| ↳ `dimensions` | json | Array of dimension values matching requested dimensions order | + +### `profound_query_fanouts` + +Query fanout report showing how AI models expand prompts into sub-queries in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | +| `categoryId` | string | Yes | Category ID \(UUID\) | +| `startDate` | string | Yes | Start date \(YYYY-MM-DD or ISO 8601\) | +| `endDate` | string | Yes | End date \(YYYY-MM-DD or ISO 8601\) | +| `metrics` | string | Yes | Comma-separated metrics: fanouts_per_execution, total_fanouts, share | +| `dimensions` | string | No | Comma-separated dimensions: prompt, query, model, region, date | +| `dateInterval` | string | No | Date interval: hour, day, week, month, year | +| `filters` | string | No | JSON array of filter objects | +| `limit` | number | No | Maximum number of results \(default 10000, max 50000\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `totalRows` | number | Total number of rows in the report | +| `data` | json | Report data rows with metrics and dimension values | +| ↳ `metrics` | json | Array of metric values matching requested metrics order | +| ↳ `dimensions` | json | Array of dimension values matching requested dimensions order | + +### `profound_prompt_answers` + +Get raw prompt answers data for a category in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | +| `categoryId` | string | Yes | Category ID \(UUID\) | +| `startDate` | string | Yes | Start date \(YYYY-MM-DD or ISO 8601\) | +| `endDate` | string | Yes | End date \(YYYY-MM-DD or ISO 8601\) | +| `filters` | string | No | JSON array of filter objects, e.g. \[\{"field":"prompt_type","operator":"is","value":"visibility"\}\] | +| `limit` | number | No | Maximum number of results \(default 10000, max 50000\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `totalRows` | number | Total number of answer rows | +| `data` | json | Raw prompt answer data | +| ↳ `prompt` | string | The prompt text | +| ↳ `promptType` | string | Prompt type \(visibility or sentiment\) | +| ↳ `response` | string | AI model response text | +| ↳ `mentions` | json | Companies/assets mentioned in the response | +| ↳ `citations` | json | URLs cited in the response | +| ↳ `topic` | string | Topic name | +| ↳ `region` | string | Region name | +| ↳ `model` | string | AI model/platform name | +| ↳ `asset` | string | Asset name | +| ↳ `createdAt` | string | Timestamp when the answer was collected | + +### `profound_bots_report` + +Query bot traffic report with hourly granularity for a domain in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | +| `domain` | string | Yes | Domain to query bot traffic for \(e.g. example.com\) | +| `startDate` | string | Yes | Start date \(YYYY-MM-DD or ISO 8601\) | +| `endDate` | string | No | End date \(YYYY-MM-DD or ISO 8601\). Defaults to now | +| `metrics` | string | Yes | Comma-separated metrics: count, citations, indexing, training, last_visit | +| `dimensions` | string | No | Comma-separated dimensions: date, hour, path, bot_name, bot_provider, bot_type | +| `dateInterval` | string | No | Date interval: hour, day, week, month, year | +| `filters` | string | No | JSON array of filter objects, e.g. \[\{"field":"bot_name","operator":"is","value":"GPTBot"\}\] | +| `limit` | number | No | Maximum number of results \(default 10000, max 50000\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `totalRows` | number | Total number of rows in the report | +| `data` | json | Report data rows with metrics and dimension values | +| ↳ `metrics` | json | Array of metric values matching requested metrics order | +| ↳ `dimensions` | json | Array of dimension values matching requested dimensions order | + +### `profound_referrals_report` + +Query human referral traffic report with hourly granularity for a domain in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | +| `domain` | string | Yes | Domain to query referral traffic for \(e.g. example.com\) | +| `startDate` | string | Yes | Start date \(YYYY-MM-DD or ISO 8601\) | +| `endDate` | string | No | End date \(YYYY-MM-DD or ISO 8601\). Defaults to now | +| `metrics` | string | Yes | Comma-separated metrics: visits, last_visit | +| `dimensions` | string | No | Comma-separated dimensions: date, hour, path, referral_source, referral_type | +| `dateInterval` | string | No | Date interval: hour, day, week, month, year | +| `filters` | string | No | JSON array of filter objects, e.g. \[\{"field":"referral_source","operator":"is","value":"openai"\}\] | +| `limit` | number | No | Maximum number of results \(default 10000, max 50000\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `totalRows` | number | Total number of rows in the report | +| `data` | json | Report data rows with metrics and dimension values | +| ↳ `metrics` | json | Array of metric values matching requested metrics order | +| ↳ `dimensions` | json | Array of dimension values matching requested dimensions order | + +### `profound_raw_logs` + +Get raw traffic logs with filters for a domain in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | +| `domain` | string | Yes | Domain to query logs for \(e.g. example.com\) | +| `startDate` | string | Yes | Start date \(YYYY-MM-DD or ISO 8601\) | +| `endDate` | string | No | End date \(YYYY-MM-DD or ISO 8601\). Defaults to now | +| `dimensions` | string | No | Comma-separated dimensions: timestamp, method, host, path, status_code, ip, user_agent, referer, bytes_sent, duration_ms, query_params | +| `filters` | string | No | JSON array of filter objects, e.g. \[\{"field":"path","operator":"contains","value":"/blog"\}\] | +| `limit` | number | No | Maximum number of results \(default 10000, max 50000\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `totalRows` | number | Total number of log entries | +| `data` | json | Log data rows with metrics and dimension values | +| ↳ `metrics` | json | Array of metric values \(count\) | +| ↳ `dimensions` | json | Array of dimension values matching requested dimensions order | + +### `profound_bot_logs` + +Get identified bot visit logs with filters for a domain in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | +| `domain` | string | Yes | Domain to query bot logs for \(e.g. example.com\) | +| `startDate` | string | Yes | Start date \(YYYY-MM-DD or ISO 8601\) | +| `endDate` | string | No | End date \(YYYY-MM-DD or ISO 8601\). Defaults to now | +| `dimensions` | string | No | Comma-separated dimensions: timestamp, method, host, path, status_code, ip, user_agent, referer, bytes_sent, duration_ms, query_params, bot_name, bot_provider, bot_types | +| `filters` | string | No | JSON array of filter objects, e.g. \[\{"field":"bot_name","operator":"is","value":"GPTBot"\}\] | +| `limit` | number | No | Maximum number of results \(default 10000, max 50000\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `totalRows` | number | Total number of bot log entries | +| `data` | json | Bot log data rows with metrics and dimension values | +| ↳ `metrics` | json | Array of metric values \(count\) | +| ↳ `dimensions` | json | Array of dimension values matching requested dimensions order | + +### `profound_list_optimizations` + +List content optimization entries for an asset in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | +| `assetId` | string | Yes | Asset ID \(UUID\) | +| `limit` | number | No | Maximum number of results \(default 10000, max 50000\) | +| `offset` | number | No | Offset for pagination \(default 0\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `totalRows` | number | Total number of optimization entries | +| `optimizations` | json | List of content optimization entries | +| ↳ `id` | string | Optimization ID \(UUID\) | +| ↳ `title` | string | Content title | +| ↳ `createdAt` | string | When the optimization was created | +| ↳ `extractedInput` | string | Extracted input text | +| ↳ `type` | string | Content type: file, text, or url | +| ↳ `status` | string | Optimization status | + +### `profound_optimization_analysis` + +Get detailed content optimization analysis for a specific content item in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | +| `assetId` | string | Yes | Asset ID \(UUID\) | +| `contentId` | string | Yes | Content/optimization ID \(UUID\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `content` | json | The analyzed content | +| ↳ `format` | string | Content format: markdown or html | +| ↳ `value` | string | Content text | +| `aeoContentScore` | json | AEO content score with target zone | +| ↳ `value` | number | AEO score value | +| ↳ `targetZone` | json | Target zone range | +| ↳ `low` | number | Low end of target range | +| ↳ `high` | number | High end of target range | +| `analysis` | json | Analysis breakdown by category | +| ↳ `breakdown` | json | Array of scoring breakdowns | +| ↳ `title` | string | Category title | +| ↳ `weight` | number | Category weight | +| ↳ `score` | number | Category score | +| `recommendations` | json | Content optimization recommendations | +| ↳ `title` | string | Recommendation title | +| ↳ `status` | string | Status: done or pending | +| ↳ `impact` | json | Impact details with section and score | +| ↳ `suggestion` | json | Suggestion text and rationale | +| ↳ `text` | string | Suggestion text | +| ↳ `rationale` | string | Why this recommendation matters | + +### `profound_prompt_volume` + +Query prompt volume data to understand search demand across AI platforms in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | +| `startDate` | string | Yes | Start date \(YYYY-MM-DD or ISO 8601\) | +| `endDate` | string | Yes | End date \(YYYY-MM-DD or ISO 8601\) | +| `metrics` | string | Yes | Comma-separated metrics: volume, change | +| `dimensions` | string | No | Comma-separated dimensions: keyword, date, platform, country_code, matching_type, frequency | +| `dateInterval` | string | No | Date interval: hour, day, week, month, year | +| `filters` | string | No | JSON array of filter objects, e.g. \[\{"field":"keyword","operator":"contains","value":"best"\}\] | +| `limit` | number | No | Maximum number of results \(default 10000, max 50000\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `totalRows` | number | Total number of rows in the report | +| `data` | json | Volume data rows with metrics and dimension values | +| ↳ `metrics` | json | Array of metric values matching requested metrics order | +| ↳ `dimensions` | json | Array of dimension values matching requested dimensions order | + +### `profound_citation_prompts` + +Get prompts that cite a specific domain across AI platforms in Profound + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Profound API Key | +| `inputDomain` | string | Yes | Domain to look up citations for \(e.g. ramp.com\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `data` | json | Citation prompt data for the queried domain | + + diff --git a/apps/sim/app/(landing)/integrations/data/icon-mapping.ts b/apps/sim/app/(landing)/integrations/data/icon-mapping.ts index 1d77fc88889..841cda375b3 100644 --- a/apps/sim/app/(landing)/integrations/data/icon-mapping.ts +++ b/apps/sim/app/(landing)/integrations/data/icon-mapping.ts @@ -126,6 +126,7 @@ import { PolymarketIcon, PostgresIcon, PosthogIcon, + ProfoundIcon, PulseIcon, QdrantIcon, QuiverIcon, @@ -302,6 +303,7 @@ export const blockTypeToIconMap: Record = { polymarket: PolymarketIcon, postgresql: PostgresIcon, posthog: PosthogIcon, + profound: ProfoundIcon, pulse_v2: PulseIcon, qdrant: QdrantIcon, quiver: QuiverIcon, diff --git a/apps/sim/app/(landing)/integrations/data/integrations.json b/apps/sim/app/(landing)/integrations/data/integrations.json index 95d2ef9ea29..2816d7c4ee0 100644 --- a/apps/sim/app/(landing)/integrations/data/integrations.json +++ b/apps/sim/app/(landing)/integrations/data/integrations.json @@ -8611,6 +8611,121 @@ "integrationType": "analytics", "tags": ["data-analytics", "monitoring"] }, + { + "type": "profound", + "slug": "profound", + "name": "Profound", + "description": "AI visibility and analytics with Profound", + "longDescription": "Track how your brand appears across AI platforms. Monitor visibility scores, sentiment, citations, bot traffic, referrals, content optimization, and prompt volumes with Profound.", + "bgColor": "#1A1A2E", + "iconName": "ProfoundIcon", + "docsUrl": "https://docs.sim.ai/tools/profound", + "operations": [ + { + "name": "List Categories", + "description": "List all organization categories in Profound" + }, + { + "name": "List Regions", + "description": "List all organization regions in Profound" + }, + { + "name": "List Models", + "description": "List all AI models/platforms tracked in Profound" + }, + { + "name": "List Domains", + "description": "List all organization domains in Profound" + }, + { + "name": "List Assets", + "description": "List all organization assets (companies/brands) across all categories in Profound" + }, + { + "name": "List Personas", + "description": "List all organization personas across all categories in Profound" + }, + { + "name": "Category Topics", + "description": "List topics for a specific category in Profound" + }, + { + "name": "Category Tags", + "description": "List tags for a specific category in Profound" + }, + { + "name": "Category Prompts", + "description": "List prompts for a specific category in Profound" + }, + { + "name": "Category Assets", + "description": "List assets (companies/brands) for a specific category in Profound" + }, + { + "name": "Category Personas", + "description": "List personas for a specific category in Profound" + }, + { + "name": "Visibility Report", + "description": "Query AI visibility report for a category in Profound" + }, + { + "name": "Sentiment Report", + "description": "Query sentiment report for a category in Profound" + }, + { + "name": "Citations Report", + "description": "Query citations report for a category in Profound" + }, + { + "name": "Query Fanouts", + "description": "Query fanout report showing how AI models expand prompts into sub-queries in Profound" + }, + { + "name": "Prompt Answers", + "description": "Get raw prompt answers data for a category in Profound" + }, + { + "name": "Bots Report", + "description": "Query bot traffic report with hourly granularity for a domain in Profound" + }, + { + "name": "Referrals Report", + "description": "Query human referral traffic report with hourly granularity for a domain in Profound" + }, + { + "name": "Raw Logs", + "description": "Get raw traffic logs with filters for a domain in Profound" + }, + { + "name": "Bot Logs", + "description": "Get identified bot visit logs with filters for a domain in Profound" + }, + { + "name": "List Optimizations", + "description": "List content optimization entries for an asset in Profound" + }, + { + "name": "Optimization Analysis", + "description": "Get detailed content optimization analysis for a specific content item in Profound" + }, + { + "name": "Prompt Volume", + "description": "Query prompt volume data to understand search demand across AI platforms in Profound" + }, + { + "name": "Citation Prompts", + "description": "Get prompts that cite a specific domain across AI platforms in Profound" + } + ], + "operationCount": 24, + "triggers": [], + "triggerCount": 0, + "authType": "api-key", + "category": "tools", + "integrationType": "analytics", + "tags": ["seo", "data-analytics"] + }, { "type": "pulse_v2", "slug": "pulse", diff --git a/apps/sim/blocks/blocks/profound.ts b/apps/sim/blocks/blocks/profound.ts new file mode 100644 index 00000000000..47bc3079440 --- /dev/null +++ b/apps/sim/blocks/blocks/profound.ts @@ -0,0 +1,406 @@ +import { ProfoundIcon } from '@/components/icons' +import type { BlockConfig } from '@/blocks/types' +import { AuthMode, IntegrationType } from '@/blocks/types' + +const CATEGORY_REPORT_OPS = [ + 'visibility_report', + 'sentiment_report', + 'citations_report', + 'prompt_answers', + 'query_fanouts', +] as const + +const DOMAIN_REPORT_OPS = ['bots_report', 'referrals_report', 'raw_logs', 'bot_logs'] as const + +const ALL_REPORT_OPS = [...CATEGORY_REPORT_OPS, ...DOMAIN_REPORT_OPS] as const + +const CATEGORY_ID_OPS = [ + ...CATEGORY_REPORT_OPS, + 'category_topics', + 'category_tags', + 'category_prompts', + 'category_assets', + 'category_personas', +] as const + +const DATE_REQUIRED_CATEGORY_OPS = [ + 'visibility_report', + 'sentiment_report', + 'citations_report', + 'prompt_answers', + 'query_fanouts', + 'prompt_volume', +] as const + +const DATE_REQUIRED_ALL_OPS = [...DATE_REQUIRED_CATEGORY_OPS, ...DOMAIN_REPORT_OPS] as const + +const METRICS_REPORT_OPS = [ + 'visibility_report', + 'sentiment_report', + 'citations_report', + 'bots_report', + 'referrals_report', + 'query_fanouts', + 'prompt_volume', +] as const + +const DIMENSION_OPS = [ + 'visibility_report', + 'sentiment_report', + 'citations_report', + 'bots_report', + 'referrals_report', + 'query_fanouts', + 'raw_logs', + 'bot_logs', + 'prompt_volume', +] as const + +const FILTER_OPS = [...ALL_REPORT_OPS, 'prompt_volume'] as const + +export const ProfoundBlock: BlockConfig = { + type: 'profound', + name: 'Profound', + description: 'AI visibility and analytics with Profound', + longDescription: + 'Track how your brand appears across AI platforms. Monitor visibility scores, sentiment, citations, bot traffic, referrals, content optimization, and prompt volumes with Profound.', + docsLink: 'https://docs.sim.ai/tools/profound', + category: 'tools', + integrationType: IntegrationType.Analytics, + tags: ['seo', 'data-analytics'], + bgColor: '#1A1A2E', + icon: ProfoundIcon, + authMode: AuthMode.ApiKey, + + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + options: [ + { label: 'List Categories', id: 'list_categories' }, + { label: 'List Regions', id: 'list_regions' }, + { label: 'List Models', id: 'list_models' }, + { label: 'List Domains', id: 'list_domains' }, + { label: 'List Assets', id: 'list_assets' }, + { label: 'List Personas', id: 'list_personas' }, + { label: 'Category Topics', id: 'category_topics' }, + { label: 'Category Tags', id: 'category_tags' }, + { label: 'Category Prompts', id: 'category_prompts' }, + { label: 'Category Assets', id: 'category_assets' }, + { label: 'Category Personas', id: 'category_personas' }, + { label: 'Visibility Report', id: 'visibility_report' }, + { label: 'Sentiment Report', id: 'sentiment_report' }, + { label: 'Citations Report', id: 'citations_report' }, + { label: 'Query Fanouts', id: 'query_fanouts' }, + { label: 'Prompt Answers', id: 'prompt_answers' }, + { label: 'Bots Report', id: 'bots_report' }, + { label: 'Referrals Report', id: 'referrals_report' }, + { label: 'Raw Logs', id: 'raw_logs' }, + { label: 'Bot Logs', id: 'bot_logs' }, + { label: 'List Optimizations', id: 'list_optimizations' }, + { label: 'Optimization Analysis', id: 'optimization_analysis' }, + { label: 'Prompt Volume', id: 'prompt_volume' }, + { label: 'Citation Prompts', id: 'citation_prompts' }, + ], + value: () => 'visibility_report', + }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + placeholder: 'Enter your Profound API key', + required: true, + password: true, + }, + + // Category ID - for category-based operations + { + id: 'categoryId', + title: 'Category ID', + type: 'short-input', + placeholder: 'Category UUID', + required: { field: 'operation', value: [...CATEGORY_ID_OPS] }, + condition: { field: 'operation', value: [...CATEGORY_ID_OPS] }, + }, + + // Domain - for domain-based operations + { + id: 'domain', + title: 'Domain', + type: 'short-input', + placeholder: 'e.g. example.com', + required: { field: 'operation', value: [...DOMAIN_REPORT_OPS] }, + condition: { field: 'operation', value: [...DOMAIN_REPORT_OPS] }, + }, + + // Input domain - for citation prompts + { + id: 'inputDomain', + title: 'Domain', + type: 'short-input', + placeholder: 'e.g. ramp.com', + required: { field: 'operation', value: 'citation_prompts' }, + condition: { field: 'operation', value: 'citation_prompts' }, + }, + + // Asset ID - for content optimization + { + id: 'assetId', + title: 'Asset ID', + type: 'short-input', + placeholder: 'Asset UUID', + required: { field: 'operation', value: ['list_optimizations', 'optimization_analysis'] }, + condition: { field: 'operation', value: ['list_optimizations', 'optimization_analysis'] }, + }, + + // Content ID - for optimization analysis + { + id: 'contentId', + title: 'Content ID', + type: 'short-input', + placeholder: 'Content/optimization UUID', + required: { field: 'operation', value: 'optimization_analysis' }, + condition: { field: 'operation', value: 'optimization_analysis' }, + }, + + // Date fields + { + id: 'startDate', + title: 'Start Date', + type: 'short-input', + placeholder: 'YYYY-MM-DD', + required: { field: 'operation', value: [...DATE_REQUIRED_ALL_OPS] }, + condition: { field: 'operation', value: [...DATE_REQUIRED_ALL_OPS] }, + wandConfig: { + enabled: true, + prompt: 'Generate a date in YYYY-MM-DD format. Return ONLY the date string.', + generationType: 'timestamp', + }, + }, + { + id: 'endDate', + title: 'End Date', + type: 'short-input', + placeholder: 'YYYY-MM-DD', + required: { field: 'operation', value: [...DATE_REQUIRED_CATEGORY_OPS] }, + condition: { field: 'operation', value: [...DATE_REQUIRED_ALL_OPS] }, + wandConfig: { + enabled: true, + prompt: 'Generate a date in YYYY-MM-DD format. Return ONLY the date string.', + generationType: 'timestamp', + }, + }, + + // Per-operation metrics fields + { + id: 'visibilityMetrics', + title: 'Metrics', + type: 'short-input', + placeholder: 'share_of_voice, visibility_score, mentions_count', + required: { field: 'operation', value: 'visibility_report' }, + condition: { field: 'operation', value: 'visibility_report' }, + }, + { + id: 'sentimentMetrics', + title: 'Metrics', + type: 'short-input', + placeholder: 'positive, negative, occurrences', + required: { field: 'operation', value: 'sentiment_report' }, + condition: { field: 'operation', value: 'sentiment_report' }, + }, + { + id: 'citationsMetrics', + title: 'Metrics', + type: 'short-input', + placeholder: 'count, citation_share', + required: { field: 'operation', value: 'citations_report' }, + condition: { field: 'operation', value: 'citations_report' }, + }, + { + id: 'botsMetrics', + title: 'Metrics', + type: 'short-input', + placeholder: 'count, citations, indexing, training', + required: { field: 'operation', value: 'bots_report' }, + condition: { field: 'operation', value: 'bots_report' }, + }, + { + id: 'referralsMetrics', + title: 'Metrics', + type: 'short-input', + placeholder: 'visits, last_visit', + required: { field: 'operation', value: 'referrals_report' }, + condition: { field: 'operation', value: 'referrals_report' }, + }, + { + id: 'fanoutsMetrics', + title: 'Metrics', + type: 'short-input', + placeholder: 'fanouts_per_execution, total_fanouts, share', + required: { field: 'operation', value: 'query_fanouts' }, + condition: { field: 'operation', value: 'query_fanouts' }, + }, + { + id: 'volumeMetrics', + title: 'Metrics', + type: 'short-input', + placeholder: 'volume, change', + required: { field: 'operation', value: 'prompt_volume' }, + condition: { field: 'operation', value: 'prompt_volume' }, + }, + + // Advanced fields + { + id: 'dimensions', + title: 'Dimensions', + type: 'short-input', + placeholder: 'e.g. date, asset_name, model', + condition: { field: 'operation', value: [...DIMENSION_OPS] }, + mode: 'advanced', + }, + { + id: 'dateInterval', + title: 'Date Interval', + type: 'dropdown', + options: [ + { label: 'Day', id: 'day' }, + { label: 'Hour', id: 'hour' }, + { label: 'Week', id: 'week' }, + { label: 'Month', id: 'month' }, + { label: 'Year', id: 'year' }, + ], + condition: { field: 'operation', value: [...METRICS_REPORT_OPS] }, + mode: 'advanced', + }, + { + id: 'filters', + title: 'Filters', + type: 'long-input', + placeholder: '[{"field":"asset_name","operator":"is","value":"Company"}]', + condition: { field: 'operation', value: [...FILTER_OPS] }, + mode: 'advanced', + wandConfig: { + enabled: true, + prompt: + 'Generate a JSON array of filter objects. Each object has "field", "operator", and "value" keys. Return ONLY valid JSON.', + generationType: 'json-object', + }, + }, + { + id: 'limit', + title: 'Limit', + type: 'short-input', + placeholder: '10000', + condition: { + field: 'operation', + value: [...FILTER_OPS, 'category_prompts', 'list_optimizations'], + }, + mode: 'advanced', + }, + + // Category prompts specific fields + { + id: 'cursor', + title: 'Cursor', + type: 'short-input', + placeholder: 'Pagination cursor from previous response', + condition: { field: 'operation', value: 'category_prompts' }, + mode: 'advanced', + }, + { + id: 'promptType', + title: 'Prompt Type', + type: 'short-input', + placeholder: 'visibility, sentiment', + condition: { field: 'operation', value: 'category_prompts' }, + mode: 'advanced', + }, + + // Optimization list specific + { + id: 'offset', + title: 'Offset', + type: 'short-input', + placeholder: '0', + condition: { field: 'operation', value: 'list_optimizations' }, + mode: 'advanced', + }, + ], + + tools: { + access: [ + 'profound_list_categories', + 'profound_list_regions', + 'profound_list_models', + 'profound_list_domains', + 'profound_list_assets', + 'profound_list_personas', + 'profound_category_topics', + 'profound_category_tags', + 'profound_category_prompts', + 'profound_category_assets', + 'profound_category_personas', + 'profound_visibility_report', + 'profound_sentiment_report', + 'profound_citations_report', + 'profound_query_fanouts', + 'profound_prompt_answers', + 'profound_bots_report', + 'profound_referrals_report', + 'profound_raw_logs', + 'profound_bot_logs', + 'profound_list_optimizations', + 'profound_optimization_analysis', + 'profound_prompt_volume', + 'profound_citation_prompts', + ], + config: { + tool: (params) => `profound_${params.operation}`, + params: (params) => { + const result: Record = {} + const metricsMap: Record = { + visibility_report: 'visibilityMetrics', + sentiment_report: 'sentimentMetrics', + citations_report: 'citationsMetrics', + bots_report: 'botsMetrics', + referrals_report: 'referralsMetrics', + query_fanouts: 'fanoutsMetrics', + prompt_volume: 'volumeMetrics', + } + const metricsField = metricsMap[params.operation as string] + if (metricsField && params[metricsField]) { + result.metrics = params[metricsField] + } + if (params.limit != null) result.limit = Number(params.limit) + if (params.offset != null) result.offset = Number(params.offset) + return result + }, + }, + }, + + inputs: { + apiKey: { type: 'string' }, + categoryId: { type: 'string' }, + domain: { type: 'string' }, + inputDomain: { type: 'string' }, + assetId: { type: 'string' }, + contentId: { type: 'string' }, + startDate: { type: 'string' }, + endDate: { type: 'string' }, + metrics: { type: 'string' }, + dimensions: { type: 'string' }, + dateInterval: { type: 'string' }, + filters: { type: 'string' }, + limit: { type: 'number' }, + offset: { type: 'number' }, + cursor: { type: 'string' }, + promptType: { type: 'string' }, + }, + + outputs: { + response: { + type: 'json', + }, + }, +} diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts index ff9fd2dc2c0..1461cd58a60 100644 --- a/apps/sim/blocks/registry.ts +++ b/apps/sim/blocks/registry.ts @@ -137,6 +137,7 @@ import { PipedriveBlock } from '@/blocks/blocks/pipedrive' import { PolymarketBlock } from '@/blocks/blocks/polymarket' import { PostgreSQLBlock } from '@/blocks/blocks/postgresql' import { PostHogBlock } from '@/blocks/blocks/posthog' +import { ProfoundBlock } from '@/blocks/blocks/profound' import { PulseBlock, PulseV2Block } from '@/blocks/blocks/pulse' import { QdrantBlock } from '@/blocks/blocks/qdrant' import { QuiverBlock } from '@/blocks/blocks/quiver' @@ -357,6 +358,7 @@ export const registry: Record = { perplexity: PerplexityBlock, pinecone: PineconeBlock, pipedrive: PipedriveBlock, + profound: ProfoundBlock, polymarket: PolymarketBlock, postgresql: PostgreSQLBlock, posthog: PostHogBlock, diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index b79a166e901..6f53db86f8b 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -1285,6 +1285,17 @@ export function StartIcon(props: SVGProps) { ) } +export function ProfoundIcon(props: SVGProps) { + return ( + + + + ) +} + export function PineconeIcon(props: SVGProps) { return ( = { + id: 'profound_bot_logs', + name: 'Profound Bot Logs', + description: 'Get identified bot visit logs with filters for a domain in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + domain: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Domain to query bot logs for (e.g. example.com)', + }, + startDate: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Start date (YYYY-MM-DD or ISO 8601)', + }, + endDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'End date (YYYY-MM-DD or ISO 8601). Defaults to now', + }, + dimensions: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Comma-separated dimensions: timestamp, method, host, path, status_code, ip, user_agent, referer, bytes_sent, duration_ms, query_params, bot_name, bot_provider, bot_types', + }, + filters: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'JSON array of filter objects, e.g. [{"field":"bot_name","operator":"is","value":"GPTBot"}]', + }, + limit: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of results (default 10000, max 50000)', + }, + }, + + request: { + url: 'https://api.tryprofound.com/v1/logs/raw/bots', + method: 'POST', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = { + domain: params.domain, + start_date: params.startDate, + metrics: ['count'], + } + if (params.endDate) { + body.end_date = params.endDate + } + if (params.dimensions) { + body.dimensions = params.dimensions.split(',').map((d) => d.trim()) + } + if (params.filters) { + try { + body.filters = JSON.parse(params.filters) + } catch { + throw new Error('Invalid JSON in filters parameter') + } + } + if (params.limit != null) { + body.pagination = { limit: params.limit } + } + return body + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to get bot logs') + } + if (Array.isArray(data)) { + return { + success: true, + output: { + totalRows: data.length, + data: data.map((row: { metrics: number[]; dimensions: string[] }) => ({ + metrics: row.metrics ?? [], + dimensions: row.dimensions ?? [], + })), + }, + } + } + return { + success: true, + output: { + totalRows: data.info?.total_rows ?? 0, + data: (data.data ?? []).map((row: { metrics: number[]; dimensions: string[] }) => ({ + metrics: row.metrics ?? [], + dimensions: row.dimensions ?? [], + })), + }, + } + }, + + outputs: { + totalRows: { + type: 'number', + description: 'Total number of bot log entries', + }, + data: { + type: 'json', + description: 'Bot log data rows with metrics and dimension values', + properties: { + metrics: { type: 'json', description: 'Array of metric values (count)' }, + dimensions: { + type: 'json', + description: 'Array of dimension values matching requested dimensions order', + }, + }, + }, + }, +} diff --git a/apps/sim/tools/profound/bots_report.ts b/apps/sim/tools/profound/bots_report.ts new file mode 100644 index 00000000000..786f3211678 --- /dev/null +++ b/apps/sim/tools/profound/bots_report.ts @@ -0,0 +1,145 @@ +import type { ToolConfig } from '@/tools/types' +import type { ProfoundBotsReportParams, ProfoundBotsReportResponse } from './types' + +export const profoundBotsReportTool: ToolConfig< + ProfoundBotsReportParams, + ProfoundBotsReportResponse +> = { + id: 'profound_bots_report', + name: 'Profound Bots Report', + description: 'Query bot traffic report with hourly granularity for a domain in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + domain: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Domain to query bot traffic for (e.g. example.com)', + }, + startDate: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Start date (YYYY-MM-DD or ISO 8601)', + }, + endDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'End date (YYYY-MM-DD or ISO 8601). Defaults to now', + }, + metrics: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Comma-separated metrics: count, citations, indexing, training, last_visit', + }, + dimensions: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated dimensions: date, hour, path, bot_name, bot_provider, bot_type', + }, + dateInterval: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Date interval: hour, day, week, month, year', + }, + filters: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'JSON array of filter objects, e.g. [{"field":"bot_name","operator":"is","value":"GPTBot"}]', + }, + limit: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of results (default 10000, max 50000)', + }, + }, + + request: { + url: 'https://api.tryprofound.com/v2/reports/bots', + method: 'POST', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = { + domain: params.domain, + start_date: params.startDate, + metrics: params.metrics.split(',').map((m) => m.trim()), + } + if (params.endDate) { + body.end_date = params.endDate + } + if (params.dimensions) { + body.dimensions = params.dimensions.split(',').map((d) => d.trim()) + } + if (params.dateInterval) { + body.date_interval = params.dateInterval + } + if (params.filters) { + try { + body.filters = JSON.parse(params.filters) + } catch { + throw new Error('Invalid JSON in filters parameter') + } + } + if (params.limit != null) { + body.pagination = { limit: params.limit } + } + return body + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to query bots report') + } + return { + success: true, + output: { + totalRows: data.info?.total_rows ?? 0, + data: (data.data ?? []).map((row: { metrics: number[]; dimensions: string[] }) => ({ + metrics: row.metrics ?? [], + dimensions: row.dimensions ?? [], + })), + }, + } + }, + + outputs: { + totalRows: { + type: 'number', + description: 'Total number of rows in the report', + }, + data: { + type: 'json', + description: 'Report data rows with metrics and dimension values', + properties: { + metrics: { + type: 'json', + description: 'Array of metric values matching requested metrics order', + }, + dimensions: { + type: 'json', + description: 'Array of dimension values matching requested dimensions order', + }, + }, + }, + }, +} diff --git a/apps/sim/tools/profound/category_assets.ts b/apps/sim/tools/profound/category_assets.ts new file mode 100644 index 00000000000..ae53f6c0fc5 --- /dev/null +++ b/apps/sim/tools/profound/category_assets.ts @@ -0,0 +1,84 @@ +import type { ToolConfig } from '@/tools/types' +import type { ProfoundCategoryAssetsParams, ProfoundCategoryAssetsResponse } from './types' + +export const profoundCategoryAssetsTool: ToolConfig< + ProfoundCategoryAssetsParams, + ProfoundCategoryAssetsResponse +> = { + id: 'profound_category_assets', + name: 'Profound Category Assets', + description: 'List assets (companies/brands) for a specific category in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + categoryId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Category ID (UUID)', + }, + }, + + request: { + url: (params) => + `https://api.tryprofound.com/v1/org/categories/${encodeURIComponent(params.categoryId)}/assets`, + method: 'GET', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + Accept: 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to list category assets') + } + return { + success: true, + output: { + assets: (data ?? []).map( + (item: { + id: string + name: string + website: string + alternate_domains: string[] | null + is_owned: boolean + created_at: string + logo_url: string + }) => ({ + id: item.id ?? null, + name: item.name ?? null, + website: item.website ?? null, + alternateDomains: item.alternate_domains ?? null, + isOwned: item.is_owned ?? false, + createdAt: item.created_at ?? null, + logoUrl: item.logo_url ?? null, + }) + ), + }, + } + }, + + outputs: { + assets: { + type: 'json', + description: 'List of assets in the category', + properties: { + id: { type: 'string', description: 'Asset ID' }, + name: { type: 'string', description: 'Asset/company name' }, + website: { type: 'string', description: 'Website URL' }, + alternateDomains: { type: 'json', description: 'Alternate domain names' }, + isOwned: { type: 'boolean', description: 'Whether the asset is owned by the organization' }, + createdAt: { type: 'string', description: 'When the asset was created' }, + logoUrl: { type: 'string', description: 'URL of the asset logo' }, + }, + }, + }, +} diff --git a/apps/sim/tools/profound/category_personas.ts b/apps/sim/tools/profound/category_personas.ts new file mode 100644 index 00000000000..5a9ec046d4d --- /dev/null +++ b/apps/sim/tools/profound/category_personas.ts @@ -0,0 +1,98 @@ +import type { ToolConfig } from '@/tools/types' +import type { ProfoundCategoryPersonasParams, ProfoundCategoryPersonasResponse } from './types' + +export const profoundCategoryPersonasTool: ToolConfig< + ProfoundCategoryPersonasParams, + ProfoundCategoryPersonasResponse +> = { + id: 'profound_category_personas', + name: 'Profound Category Personas', + description: 'List personas for a specific category in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + categoryId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Category ID (UUID)', + }, + }, + + request: { + url: (params) => + `https://api.tryprofound.com/v1/org/categories/${encodeURIComponent(params.categoryId)}/personas`, + method: 'GET', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + Accept: 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to list category personas') + } + return { + success: true, + output: { + personas: (data.data ?? []).map( + (item: { + id: string + name: string + persona: { + behavior: { painPoints: string | null; motivations: string | null } + employment: { + industry: string[] + jobTitle: string[] + companySize: string[] + roleSeniority: string[] + } + demographics: { ageRange: string[] } + } + }) => ({ + id: item.id ?? null, + name: item.name ?? null, + persona: { + behavior: { + painPoints: item.persona?.behavior?.painPoints ?? null, + motivations: item.persona?.behavior?.motivations ?? null, + }, + employment: { + industry: item.persona?.employment?.industry ?? [], + jobTitle: item.persona?.employment?.jobTitle ?? [], + companySize: item.persona?.employment?.companySize ?? [], + roleSeniority: item.persona?.employment?.roleSeniority ?? [], + }, + demographics: { + ageRange: item.persona?.demographics?.ageRange ?? [], + }, + }, + }) + ), + }, + } + }, + + outputs: { + personas: { + type: 'json', + description: 'List of personas in the category', + properties: { + id: { type: 'string', description: 'Persona ID' }, + name: { type: 'string', description: 'Persona name' }, + persona: { + type: 'json', + description: 'Persona profile with behavior, employment, and demographics', + }, + }, + }, + }, +} diff --git a/apps/sim/tools/profound/category_prompts.ts b/apps/sim/tools/profound/category_prompts.ts new file mode 100644 index 00000000000..7ac031164c3 --- /dev/null +++ b/apps/sim/tools/profound/category_prompts.ts @@ -0,0 +1,189 @@ +import type { ToolConfig } from '@/tools/types' +import type { ProfoundCategoryPromptsParams, ProfoundCategoryPromptsResponse } from './types' + +export const profoundCategoryPromptsTool: ToolConfig< + ProfoundCategoryPromptsParams, + ProfoundCategoryPromptsResponse +> = { + id: 'profound_category_prompts', + name: 'Profound Category Prompts', + description: 'List prompts for a specific category in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + categoryId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Category ID (UUID)', + }, + limit: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of results (default 10000, max 10000)', + }, + cursor: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Pagination cursor from previous response', + }, + orderDir: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Sort direction: asc or desc (default desc)', + }, + promptType: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated prompt types to filter: visibility, sentiment', + }, + topicId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated topic IDs (UUIDs) to filter by', + }, + tagId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated tag IDs (UUIDs) to filter by', + }, + regionId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated region IDs (UUIDs) to filter by', + }, + platformId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated platform IDs (UUIDs) to filter by', + }, + }, + + request: { + url: (params) => { + const url = new URL( + `https://api.tryprofound.com/v1/org/categories/${encodeURIComponent(params.categoryId)}/prompts` + ) + if (params.limit != null) url.searchParams.set('limit', String(params.limit)) + if (params.cursor) url.searchParams.set('cursor', params.cursor) + if (params.orderDir) url.searchParams.set('order_dir', params.orderDir) + if (params.promptType) { + for (const pt of params.promptType.split(',').map((s) => s.trim())) { + url.searchParams.append('prompt_type', pt) + } + } + if (params.topicId) { + for (const tid of params.topicId.split(',').map((s) => s.trim())) { + url.searchParams.append('topic_id', tid) + } + } + if (params.tagId) { + for (const tid of params.tagId.split(',').map((s) => s.trim())) { + url.searchParams.append('tag_id', tid) + } + } + if (params.regionId) { + for (const rid of params.regionId.split(',').map((s) => s.trim())) { + url.searchParams.append('region_id', rid) + } + } + if (params.platformId) { + for (const pid of params.platformId.split(',').map((s) => s.trim())) { + url.searchParams.append('platform_id', pid) + } + } + return url.toString() + }, + method: 'GET', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + Accept: 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to list category prompts') + } + return { + success: true, + output: { + totalRows: data.info?.total_rows ?? 0, + nextCursor: data.info?.next_cursor ?? null, + prompts: (data.data ?? []).map( + (item: { + id: string + prompt: string + prompt_type: string + topic: { id: string; name: string } + tags: Array<{ id: string; name: string }> + regions: Array<{ id: string; name: string }> + platforms: Array<{ id: string; name: string }> + created_at: string + }) => ({ + id: item.id ?? null, + prompt: item.prompt ?? null, + promptType: item.prompt_type ?? null, + topicId: item.topic?.id ?? null, + topicName: item.topic?.name ?? null, + tags: (item.tags ?? []).map((t: { id: string; name: string }) => ({ + id: t.id ?? null, + name: t.name ?? null, + })), + regions: (item.regions ?? []).map((r: { id: string; name: string }) => ({ + id: r.id ?? null, + name: r.name ?? null, + })), + platforms: (item.platforms ?? []).map((p: { id: string; name: string }) => ({ + id: p.id ?? null, + name: p.name ?? null, + })), + createdAt: item.created_at ?? null, + }) + ), + }, + } + }, + + outputs: { + totalRows: { + type: 'number', + description: 'Total number of prompts', + }, + nextCursor: { + type: 'string', + description: 'Cursor for next page of results', + optional: true, + }, + prompts: { + type: 'json', + description: 'List of prompts', + properties: { + id: { type: 'string', description: 'Prompt ID' }, + prompt: { type: 'string', description: 'Prompt text' }, + promptType: { type: 'string', description: 'Prompt type (visibility or sentiment)' }, + topicId: { type: 'string', description: 'Topic ID' }, + topicName: { type: 'string', description: 'Topic name' }, + tags: { type: 'json', description: 'Associated tags' }, + regions: { type: 'json', description: 'Associated regions' }, + platforms: { type: 'json', description: 'Associated platforms' }, + createdAt: { type: 'string', description: 'When the prompt was created' }, + }, + }, + }, +} diff --git a/apps/sim/tools/profound/category_tags.ts b/apps/sim/tools/profound/category_tags.ts new file mode 100644 index 00000000000..138e5f21351 --- /dev/null +++ b/apps/sim/tools/profound/category_tags.ts @@ -0,0 +1,64 @@ +import type { ToolConfig } from '@/tools/types' +import type { ProfoundCategoryTagsParams, ProfoundCategoryTagsResponse } from './types' + +export const profoundCategoryTagsTool: ToolConfig< + ProfoundCategoryTagsParams, + ProfoundCategoryTagsResponse +> = { + id: 'profound_category_tags', + name: 'Profound Category Tags', + description: 'List tags for a specific category in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + categoryId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Category ID (UUID)', + }, + }, + + request: { + url: (params) => + `https://api.tryprofound.com/v1/org/categories/${encodeURIComponent(params.categoryId)}/tags`, + method: 'GET', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + Accept: 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to list category tags') + } + return { + success: true, + output: { + tags: (data ?? []).map((item: { id: string; name: string }) => ({ + id: item.id ?? null, + name: item.name ?? null, + })), + }, + } + }, + + outputs: { + tags: { + type: 'json', + description: 'List of tags in the category', + properties: { + id: { type: 'string', description: 'Tag ID (UUID)' }, + name: { type: 'string', description: 'Tag name' }, + }, + }, + }, +} diff --git a/apps/sim/tools/profound/category_topics.ts b/apps/sim/tools/profound/category_topics.ts new file mode 100644 index 00000000000..e39d51ffb66 --- /dev/null +++ b/apps/sim/tools/profound/category_topics.ts @@ -0,0 +1,64 @@ +import type { ToolConfig } from '@/tools/types' +import type { ProfoundCategoryTopicsParams, ProfoundCategoryTopicsResponse } from './types' + +export const profoundCategoryTopicsTool: ToolConfig< + ProfoundCategoryTopicsParams, + ProfoundCategoryTopicsResponse +> = { + id: 'profound_category_topics', + name: 'Profound Category Topics', + description: 'List topics for a specific category in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + categoryId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Category ID (UUID)', + }, + }, + + request: { + url: (params) => + `https://api.tryprofound.com/v1/org/categories/${encodeURIComponent(params.categoryId)}/topics`, + method: 'GET', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + Accept: 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to list category topics') + } + return { + success: true, + output: { + topics: (data ?? []).map((item: { id: string; name: string }) => ({ + id: item.id ?? null, + name: item.name ?? null, + })), + }, + } + }, + + outputs: { + topics: { + type: 'json', + description: 'List of topics in the category', + properties: { + id: { type: 'string', description: 'Topic ID (UUID)' }, + name: { type: 'string', description: 'Topic name' }, + }, + }, + }, +} diff --git a/apps/sim/tools/profound/citation_prompts.ts b/apps/sim/tools/profound/citation_prompts.ts new file mode 100644 index 00000000000..31b02b68677 --- /dev/null +++ b/apps/sim/tools/profound/citation_prompts.ts @@ -0,0 +1,60 @@ +import type { ToolConfig } from '@/tools/types' +import type { ProfoundCitationPromptsParams, ProfoundCitationPromptsResponse } from './types' + +export const profoundCitationPromptsTool: ToolConfig< + ProfoundCitationPromptsParams, + ProfoundCitationPromptsResponse +> = { + id: 'profound_citation_prompts', + name: 'Profound Citation Prompts', + description: 'Get prompts that cite a specific domain across AI platforms in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + inputDomain: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Domain to look up citations for (e.g. ramp.com)', + }, + }, + + request: { + url: (params) => { + const url = new URL('https://api.tryprofound.com/v1/prompt-volumes/citation-prompts') + url.searchParams.set('input_domain', params.inputDomain) + return url.toString() + }, + method: 'GET', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + Accept: 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to get citation prompts') + } + return { + success: true, + output: { + data: data ?? null, + }, + } + }, + + outputs: { + data: { + type: 'json', + description: 'Citation prompt data for the queried domain', + }, + }, +} diff --git a/apps/sim/tools/profound/citations_report.ts b/apps/sim/tools/profound/citations_report.ts new file mode 100644 index 00000000000..dc3b83b76bd --- /dev/null +++ b/apps/sim/tools/profound/citations_report.ts @@ -0,0 +1,144 @@ +import type { ToolConfig } from '@/tools/types' +import type { ProfoundCitationsReportParams, ProfoundCitationsReportResponse } from './types' + +export const profoundCitationsReportTool: ToolConfig< + ProfoundCitationsReportParams, + ProfoundCitationsReportResponse +> = { + id: 'profound_citations_report', + name: 'Profound Citations Report', + description: 'Query citations report for a category in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + categoryId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Category ID (UUID)', + }, + startDate: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Start date (YYYY-MM-DD or ISO 8601)', + }, + endDate: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'End date (YYYY-MM-DD or ISO 8601)', + }, + metrics: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Comma-separated metrics: count, citation_share', + }, + dimensions: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Comma-separated dimensions: hostname, path, date, region, topic, model, tag, prompt, url, root_domain, persona, citation_category', + }, + dateInterval: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Date interval: hour, day, week, month, year', + }, + filters: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'JSON array of filter objects, e.g. [{"field":"hostname","operator":"is","value":"example.com"}]', + }, + limit: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of results (default 10000, max 50000)', + }, + }, + + request: { + url: 'https://api.tryprofound.com/v1/reports/citations', + method: 'POST', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = { + category_id: params.categoryId, + start_date: params.startDate, + end_date: params.endDate, + metrics: params.metrics.split(',').map((m) => m.trim()), + } + if (params.dimensions) { + body.dimensions = params.dimensions.split(',').map((d) => d.trim()) + } + if (params.dateInterval) { + body.date_interval = params.dateInterval + } + if (params.filters) { + try { + body.filters = JSON.parse(params.filters) + } catch { + throw new Error('Invalid JSON in filters parameter') + } + } + if (params.limit != null) { + body.pagination = { limit: params.limit } + } + return body + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to query citations report') + } + return { + success: true, + output: { + totalRows: data.info?.total_rows ?? 0, + data: (data.data ?? []).map((row: { metrics: number[]; dimensions: string[] }) => ({ + metrics: row.metrics ?? [], + dimensions: row.dimensions ?? [], + })), + }, + } + }, + + outputs: { + totalRows: { + type: 'number', + description: 'Total number of rows in the report', + }, + data: { + type: 'json', + description: 'Report data rows with metrics and dimension values', + properties: { + metrics: { + type: 'json', + description: 'Array of metric values matching requested metrics order', + }, + dimensions: { + type: 'json', + description: 'Array of dimension values matching requested dimensions order', + }, + }, + }, + }, +} diff --git a/apps/sim/tools/profound/index.ts b/apps/sim/tools/profound/index.ts new file mode 100644 index 00000000000..7c4ef058d79 --- /dev/null +++ b/apps/sim/tools/profound/index.ts @@ -0,0 +1,24 @@ +export { profoundBotLogsTool } from './bot_logs' +export { profoundBotsReportTool } from './bots_report' +export { profoundCategoryAssetsTool } from './category_assets' +export { profoundCategoryPersonasTool } from './category_personas' +export { profoundCategoryPromptsTool } from './category_prompts' +export { profoundCategoryTagsTool } from './category_tags' +export { profoundCategoryTopicsTool } from './category_topics' +export { profoundCitationPromptsTool } from './citation_prompts' +export { profoundCitationsReportTool } from './citations_report' +export { profoundListAssetsTool } from './list_assets' +export { profoundListCategoriesTool } from './list_categories' +export { profoundListDomainsTool } from './list_domains' +export { profoundListModelsTool } from './list_models' +export { profoundListOptimizationsTool } from './list_optimizations' +export { profoundListPersonasTool } from './list_personas' +export { profoundListRegionsTool } from './list_regions' +export { profoundOptimizationAnalysisTool } from './optimization_analysis' +export { profoundPromptAnswersTool } from './prompt_answers' +export { profoundPromptVolumeTool } from './prompt_volume' +export { profoundQueryFanoutsTool } from './query_fanouts' +export { profoundRawLogsTool } from './raw_logs' +export { profoundReferralsReportTool } from './referrals_report' +export { profoundSentimentReportTool } from './sentiment_report' +export { profoundVisibilityReportTool } from './visibility_report' diff --git a/apps/sim/tools/profound/list_assets.ts b/apps/sim/tools/profound/list_assets.ts new file mode 100644 index 00000000000..b528a9c013b --- /dev/null +++ b/apps/sim/tools/profound/list_assets.ts @@ -0,0 +1,85 @@ +import type { ToolConfig } from '@/tools/types' +import type { ProfoundListAssetsParams, ProfoundListAssetsResponse } from './types' + +export const profoundListAssetsTool: ToolConfig< + ProfoundListAssetsParams, + ProfoundListAssetsResponse +> = { + id: 'profound_list_assets', + name: 'Profound List Assets', + description: 'List all organization assets (companies/brands) across all categories in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + }, + + request: { + url: 'https://api.tryprofound.com/v1/org/assets', + method: 'GET', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + Accept: 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to list assets') + } + return { + success: true, + output: { + assets: (data.data ?? []).map( + (item: { + id: string + name: string + website: string + alternate_domains: string[] | null + is_owned: boolean + created_at: string + logo_url: string + category: { id: string; name: string } + }) => ({ + id: item.id ?? null, + name: item.name ?? null, + website: item.website ?? null, + alternateDomains: item.alternate_domains ?? null, + isOwned: item.is_owned ?? false, + createdAt: item.created_at ?? null, + logoUrl: item.logo_url ?? null, + categoryId: item.category?.id ?? null, + categoryName: item.category?.name ?? null, + }) + ), + }, + } + }, + + outputs: { + assets: { + type: 'json', + description: 'List of organization assets with category info', + properties: { + id: { type: 'string', description: 'Asset ID' }, + name: { type: 'string', description: 'Asset/company name' }, + website: { type: 'string', description: 'Asset website URL' }, + alternateDomains: { type: 'json', description: 'Alternate domain names' }, + isOwned: { + type: 'boolean', + description: 'Whether this asset is owned by the organization', + }, + createdAt: { type: 'string', description: 'When the asset was created' }, + logoUrl: { type: 'string', description: 'URL of the asset logo' }, + categoryId: { type: 'string', description: 'Category ID the asset belongs to' }, + categoryName: { type: 'string', description: 'Category name' }, + }, + }, + }, +} diff --git a/apps/sim/tools/profound/list_categories.ts b/apps/sim/tools/profound/list_categories.ts new file mode 100644 index 00000000000..29f5ab5eb17 --- /dev/null +++ b/apps/sim/tools/profound/list_categories.ts @@ -0,0 +1,57 @@ +import type { ToolConfig } from '@/tools/types' +import type { ProfoundListCategoriesParams, ProfoundListCategoriesResponse } from './types' + +export const profoundListCategoriesTool: ToolConfig< + ProfoundListCategoriesParams, + ProfoundListCategoriesResponse +> = { + id: 'profound_list_categories', + name: 'Profound List Categories', + description: 'List all organization categories in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + }, + + request: { + url: 'https://api.tryprofound.com/v1/org/categories', + method: 'GET', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + Accept: 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to list categories') + } + return { + success: true, + output: { + categories: (data ?? []).map((item: { id: string; name: string }) => ({ + id: item.id ?? null, + name: item.name ?? null, + })), + }, + } + }, + + outputs: { + categories: { + type: 'json', + description: 'List of organization categories', + properties: { + id: { type: 'string', description: 'Category ID' }, + name: { type: 'string', description: 'Category name' }, + }, + }, + }, +} diff --git a/apps/sim/tools/profound/list_domains.ts b/apps/sim/tools/profound/list_domains.ts new file mode 100644 index 00000000000..b723bffbcb7 --- /dev/null +++ b/apps/sim/tools/profound/list_domains.ts @@ -0,0 +1,59 @@ +import type { ToolConfig } from '@/tools/types' +import type { ProfoundListDomainsParams, ProfoundListDomainsResponse } from './types' + +export const profoundListDomainsTool: ToolConfig< + ProfoundListDomainsParams, + ProfoundListDomainsResponse +> = { + id: 'profound_list_domains', + name: 'Profound List Domains', + description: 'List all organization domains in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + }, + + request: { + url: 'https://api.tryprofound.com/v1/org/domains', + method: 'GET', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + Accept: 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to list domains') + } + return { + success: true, + output: { + domains: (data ?? []).map((item: { id: string; name: string; created_at: string }) => ({ + id: item.id ?? null, + name: item.name ?? null, + createdAt: item.created_at ?? null, + })), + }, + } + }, + + outputs: { + domains: { + type: 'json', + description: 'List of organization domains', + properties: { + id: { type: 'string', description: 'Domain ID (UUID)' }, + name: { type: 'string', description: 'Domain name' }, + createdAt: { type: 'string', description: 'When the domain was added' }, + }, + }, + }, +} diff --git a/apps/sim/tools/profound/list_models.ts b/apps/sim/tools/profound/list_models.ts new file mode 100644 index 00000000000..a1cc9da54a4 --- /dev/null +++ b/apps/sim/tools/profound/list_models.ts @@ -0,0 +1,57 @@ +import type { ToolConfig } from '@/tools/types' +import type { ProfoundListModelsParams, ProfoundListModelsResponse } from './types' + +export const profoundListModelsTool: ToolConfig< + ProfoundListModelsParams, + ProfoundListModelsResponse +> = { + id: 'profound_list_models', + name: 'Profound List Models', + description: 'List all AI models/platforms tracked in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + }, + + request: { + url: 'https://api.tryprofound.com/v1/org/models', + method: 'GET', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + Accept: 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to list models') + } + return { + success: true, + output: { + models: (data ?? []).map((item: { id: string; name: string }) => ({ + id: item.id ?? null, + name: item.name ?? null, + })), + }, + } + }, + + outputs: { + models: { + type: 'json', + description: 'List of AI models/platforms', + properties: { + id: { type: 'string', description: 'Model ID (UUID)' }, + name: { type: 'string', description: 'Model/platform name' }, + }, + }, + }, +} diff --git a/apps/sim/tools/profound/list_optimizations.ts b/apps/sim/tools/profound/list_optimizations.ts new file mode 100644 index 00000000000..ca072e9bf71 --- /dev/null +++ b/apps/sim/tools/profound/list_optimizations.ts @@ -0,0 +1,104 @@ +import type { ToolConfig } from '@/tools/types' +import type { ProfoundListOptimizationsParams, ProfoundListOptimizationsResponse } from './types' + +export const profoundListOptimizationsTool: ToolConfig< + ProfoundListOptimizationsParams, + ProfoundListOptimizationsResponse +> = { + id: 'profound_list_optimizations', + name: 'Profound List Optimizations', + description: 'List content optimization entries for an asset in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + assetId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Asset ID (UUID)', + }, + limit: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of results (default 10000, max 50000)', + }, + offset: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Offset for pagination (default 0)', + }, + }, + + request: { + url: (params) => { + const url = new URL( + `https://api.tryprofound.com/v1/content/${encodeURIComponent(params.assetId)}/optimization` + ) + if (params.limit != null) url.searchParams.set('limit', String(params.limit)) + if (params.offset != null) url.searchParams.set('offset', String(params.offset)) + return url.toString() + }, + method: 'GET', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + Accept: 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to list optimizations') + } + return { + success: true, + output: { + totalRows: data.info?.total_rows ?? 0, + optimizations: (data.data ?? []).map( + (item: { + id: string + title: string + created_at: string + extracted_input: string | null + type: string + status: string + }) => ({ + id: item.id ?? null, + title: item.title ?? null, + createdAt: item.created_at ?? null, + extractedInput: item.extracted_input ?? null, + type: item.type ?? null, + status: item.status ?? null, + }) + ), + }, + } + }, + + outputs: { + totalRows: { + type: 'number', + description: 'Total number of optimization entries', + }, + optimizations: { + type: 'json', + description: 'List of content optimization entries', + properties: { + id: { type: 'string', description: 'Optimization ID (UUID)' }, + title: { type: 'string', description: 'Content title' }, + createdAt: { type: 'string', description: 'When the optimization was created' }, + extractedInput: { type: 'string', description: 'Extracted input text' }, + type: { type: 'string', description: 'Content type: file, text, or url' }, + status: { type: 'string', description: 'Optimization status' }, + }, + }, + }, +} diff --git a/apps/sim/tools/profound/list_personas.ts b/apps/sim/tools/profound/list_personas.ts new file mode 100644 index 00000000000..31aac7b8dc6 --- /dev/null +++ b/apps/sim/tools/profound/list_personas.ts @@ -0,0 +1,96 @@ +import type { ToolConfig } from '@/tools/types' +import type { ProfoundListPersonasParams, ProfoundListPersonasResponse } from './types' + +export const profoundListPersonasTool: ToolConfig< + ProfoundListPersonasParams, + ProfoundListPersonasResponse +> = { + id: 'profound_list_personas', + name: 'Profound List Personas', + description: 'List all organization personas across all categories in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + }, + + request: { + url: 'https://api.tryprofound.com/v1/org/personas', + method: 'GET', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + Accept: 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to list personas') + } + return { + success: true, + output: { + personas: (data.data ?? []).map( + (item: { + id: string + name: string + category: { id: string; name: string } + persona: { + behavior: { painPoints: string | null; motivations: string | null } + employment: { + industry: string[] + jobTitle: string[] + companySize: string[] + roleSeniority: string[] + } + demographics: { ageRange: string[] } + } + }) => ({ + id: item.id ?? null, + name: item.name ?? null, + categoryId: item.category?.id ?? null, + categoryName: item.category?.name ?? null, + persona: { + behavior: { + painPoints: item.persona?.behavior?.painPoints ?? null, + motivations: item.persona?.behavior?.motivations ?? null, + }, + employment: { + industry: item.persona?.employment?.industry ?? [], + jobTitle: item.persona?.employment?.jobTitle ?? [], + companySize: item.persona?.employment?.companySize ?? [], + roleSeniority: item.persona?.employment?.roleSeniority ?? [], + }, + demographics: { + ageRange: item.persona?.demographics?.ageRange ?? [], + }, + }, + }) + ), + }, + } + }, + + outputs: { + personas: { + type: 'json', + description: 'List of organization personas with profile details', + properties: { + id: { type: 'string', description: 'Persona ID' }, + name: { type: 'string', description: 'Persona name' }, + categoryId: { type: 'string', description: 'Category ID' }, + categoryName: { type: 'string', description: 'Category name' }, + persona: { + type: 'json', + description: 'Persona profile with behavior, employment, and demographics', + }, + }, + }, + }, +} diff --git a/apps/sim/tools/profound/list_regions.ts b/apps/sim/tools/profound/list_regions.ts new file mode 100644 index 00000000000..f3cc60b5535 --- /dev/null +++ b/apps/sim/tools/profound/list_regions.ts @@ -0,0 +1,57 @@ +import type { ToolConfig } from '@/tools/types' +import type { ProfoundListRegionsParams, ProfoundListRegionsResponse } from './types' + +export const profoundListRegionsTool: ToolConfig< + ProfoundListRegionsParams, + ProfoundListRegionsResponse +> = { + id: 'profound_list_regions', + name: 'Profound List Regions', + description: 'List all organization regions in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + }, + + request: { + url: 'https://api.tryprofound.com/v1/org/regions', + method: 'GET', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + Accept: 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to list regions') + } + return { + success: true, + output: { + regions: (data ?? []).map((item: { id: string; name: string }) => ({ + id: item.id ?? null, + name: item.name ?? null, + })), + }, + } + }, + + outputs: { + regions: { + type: 'json', + description: 'List of organization regions', + properties: { + id: { type: 'string', description: 'Region ID (UUID)' }, + name: { type: 'string', description: 'Region name' }, + }, + }, + }, +} diff --git a/apps/sim/tools/profound/optimization_analysis.ts b/apps/sim/tools/profound/optimization_analysis.ts new file mode 100644 index 00000000000..b2d64606384 --- /dev/null +++ b/apps/sim/tools/profound/optimization_analysis.ts @@ -0,0 +1,161 @@ +import type { ToolConfig } from '@/tools/types' +import type { + ProfoundOptimizationAnalysisParams, + ProfoundOptimizationAnalysisResponse, +} from './types' + +export const profoundOptimizationAnalysisTool: ToolConfig< + ProfoundOptimizationAnalysisParams, + ProfoundOptimizationAnalysisResponse +> = { + id: 'profound_optimization_analysis', + name: 'Profound Optimization Analysis', + description: 'Get detailed content optimization analysis for a specific content item in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + assetId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Asset ID (UUID)', + }, + contentId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Content/optimization ID (UUID)', + }, + }, + + request: { + url: (params) => + `https://api.tryprofound.com/v1/content/${encodeURIComponent(params.assetId)}/optimization/${encodeURIComponent(params.contentId)}`, + method: 'GET', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + Accept: 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to get optimization analysis') + } + const analysis = data.data + return { + success: true, + output: { + content: { + format: analysis?.content?.format ?? null, + value: analysis?.content?.value ?? null, + }, + aeoContentScore: analysis?.aeo_content_score + ? { + value: analysis.aeo_content_score.value ?? 0, + targetZone: { + low: analysis.aeo_content_score.target_zone?.low ?? 0, + high: analysis.aeo_content_score.target_zone?.high ?? 0, + }, + } + : null, + analysis: { + breakdown: (analysis?.analysis?.breakdown ?? []).map( + (b: { title: string; weight: number; score: number }) => ({ + title: b.title ?? null, + weight: b.weight ?? 0, + score: b.score ?? 0, + }) + ), + }, + recommendations: (analysis?.recommendations ?? []).map( + (r: { + title: string + status: string + impact: { section: string; score: number } | null + suggestion: { text: string; rationale: string } + }) => ({ + title: r.title ?? null, + status: r.status ?? null, + impact: r.impact + ? { + section: r.impact.section ?? null, + score: r.impact.score ?? 0, + } + : null, + suggestion: { + text: r.suggestion?.text ?? null, + rationale: r.suggestion?.rationale ?? null, + }, + }) + ), + }, + } + }, + + outputs: { + content: { + type: 'json', + description: 'The analyzed content', + properties: { + format: { type: 'string', description: 'Content format: markdown or html' }, + value: { type: 'string', description: 'Content text' }, + }, + }, + aeoContentScore: { + type: 'json', + description: 'AEO content score with target zone', + optional: true, + properties: { + value: { type: 'number', description: 'AEO score value' }, + targetZone: { + type: 'json', + description: 'Target zone range', + properties: { + low: { type: 'number', description: 'Low end of target range' }, + high: { type: 'number', description: 'High end of target range' }, + }, + }, + }, + }, + analysis: { + type: 'json', + description: 'Analysis breakdown by category', + properties: { + breakdown: { + type: 'json', + description: 'Array of scoring breakdowns', + properties: { + title: { type: 'string', description: 'Category title' }, + weight: { type: 'number', description: 'Category weight' }, + score: { type: 'number', description: 'Category score' }, + }, + }, + }, + }, + recommendations: { + type: 'json', + description: 'Content optimization recommendations', + properties: { + title: { type: 'string', description: 'Recommendation title' }, + status: { type: 'string', description: 'Status: done or pending' }, + impact: { type: 'json', description: 'Impact details with section and score' }, + suggestion: { + type: 'json', + description: 'Suggestion text and rationale', + properties: { + text: { type: 'string', description: 'Suggestion text' }, + rationale: { type: 'string', description: 'Why this recommendation matters' }, + }, + }, + }, + }, + }, +} diff --git a/apps/sim/tools/profound/prompt_answers.ts b/apps/sim/tools/profound/prompt_answers.ts new file mode 100644 index 00000000000..1626c6aca67 --- /dev/null +++ b/apps/sim/tools/profound/prompt_answers.ts @@ -0,0 +1,141 @@ +import type { ToolConfig } from '@/tools/types' +import type { ProfoundPromptAnswersParams, ProfoundPromptAnswersResponse } from './types' + +export const profoundPromptAnswersTool: ToolConfig< + ProfoundPromptAnswersParams, + ProfoundPromptAnswersResponse +> = { + id: 'profound_prompt_answers', + name: 'Profound Prompt Answers', + description: 'Get raw prompt answers data for a category in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + categoryId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Category ID (UUID)', + }, + startDate: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Start date (YYYY-MM-DD or ISO 8601)', + }, + endDate: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'End date (YYYY-MM-DD or ISO 8601)', + }, + filters: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'JSON array of filter objects, e.g. [{"field":"prompt_type","operator":"is","value":"visibility"}]', + }, + limit: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of results (default 10000, max 50000)', + }, + }, + + request: { + url: 'https://api.tryprofound.com/v1/prompts/answers', + method: 'POST', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = { + category_id: params.categoryId, + start_date: params.startDate, + end_date: params.endDate, + } + if (params.filters) { + try { + body.filters = JSON.parse(params.filters) + } catch { + throw new Error('Invalid JSON in filters parameter') + } + } + if (params.limit != null) { + body.pagination = { limit: params.limit } + } + return body + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to get prompt answers') + } + return { + success: true, + output: { + totalRows: data.info?.total_rows ?? 0, + data: (data.data ?? []).map( + (row: { + prompt: string | null + prompt_type: string | null + response: string | null + mentions: string[] | null + citations: string[] | null + topic: string | null + region: string | null + model: string | null + asset: string | null + created_at: string | null + }) => ({ + prompt: row.prompt ?? null, + promptType: row.prompt_type ?? null, + response: row.response ?? null, + mentions: row.mentions ?? [], + citations: row.citations ?? [], + topic: row.topic ?? null, + region: row.region ?? null, + model: row.model ?? null, + asset: row.asset ?? null, + createdAt: row.created_at ?? null, + }) + ), + }, + } + }, + + outputs: { + totalRows: { + type: 'number', + description: 'Total number of answer rows', + }, + data: { + type: 'json', + description: 'Raw prompt answer data', + properties: { + prompt: { type: 'string', description: 'The prompt text' }, + promptType: { type: 'string', description: 'Prompt type (visibility or sentiment)' }, + response: { type: 'string', description: 'AI model response text' }, + mentions: { type: 'json', description: 'Companies/assets mentioned in the response' }, + citations: { type: 'json', description: 'URLs cited in the response' }, + topic: { type: 'string', description: 'Topic name' }, + region: { type: 'string', description: 'Region name' }, + model: { type: 'string', description: 'AI model/platform name' }, + asset: { type: 'string', description: 'Asset name' }, + createdAt: { type: 'string', description: 'Timestamp when the answer was collected' }, + }, + }, + }, +} diff --git a/apps/sim/tools/profound/prompt_volume.ts b/apps/sim/tools/profound/prompt_volume.ts new file mode 100644 index 00000000000..904b6284d82 --- /dev/null +++ b/apps/sim/tools/profound/prompt_volume.ts @@ -0,0 +1,138 @@ +import type { ToolConfig } from '@/tools/types' +import type { ProfoundPromptVolumeParams, ProfoundPromptVolumeResponse } from './types' + +export const profoundPromptVolumeTool: ToolConfig< + ProfoundPromptVolumeParams, + ProfoundPromptVolumeResponse +> = { + id: 'profound_prompt_volume', + name: 'Profound Prompt Volume', + description: + 'Query prompt volume data to understand search demand across AI platforms in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + startDate: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Start date (YYYY-MM-DD or ISO 8601)', + }, + endDate: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'End date (YYYY-MM-DD or ISO 8601)', + }, + metrics: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Comma-separated metrics: volume, change', + }, + dimensions: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Comma-separated dimensions: keyword, date, platform, country_code, matching_type, frequency', + }, + dateInterval: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Date interval: hour, day, week, month, year', + }, + filters: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'JSON array of filter objects, e.g. [{"field":"keyword","operator":"contains","value":"best"}]', + }, + limit: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of results (default 10000, max 50000)', + }, + }, + + request: { + url: 'https://api.tryprofound.com/v1/prompt-volumes/volume', + method: 'POST', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = { + start_date: params.startDate, + end_date: params.endDate, + metrics: params.metrics.split(',').map((m) => m.trim()), + } + if (params.dimensions) { + body.dimensions = params.dimensions.split(',').map((d) => d.trim()) + } + if (params.dateInterval) { + body.date_interval = params.dateInterval + } + if (params.filters) { + try { + body.filters = JSON.parse(params.filters) + } catch { + throw new Error('Invalid JSON in filters parameter') + } + } + if (params.limit != null) { + body.pagination = { limit: params.limit } + } + return body + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to query prompt volume') + } + return { + success: true, + output: { + totalRows: data.info?.total_rows ?? 0, + data: (data.data ?? []).map((row: { metrics: number[]; dimensions: string[] }) => ({ + metrics: row.metrics ?? [], + dimensions: row.dimensions ?? [], + })), + }, + } + }, + + outputs: { + totalRows: { + type: 'number', + description: 'Total number of rows in the report', + }, + data: { + type: 'json', + description: 'Volume data rows with metrics and dimension values', + properties: { + metrics: { + type: 'json', + description: 'Array of metric values matching requested metrics order', + }, + dimensions: { + type: 'json', + description: 'Array of dimension values matching requested dimensions order', + }, + }, + }, + }, +} diff --git a/apps/sim/tools/profound/query_fanouts.ts b/apps/sim/tools/profound/query_fanouts.ts new file mode 100644 index 00000000000..cbb46b1c18a --- /dev/null +++ b/apps/sim/tools/profound/query_fanouts.ts @@ -0,0 +1,143 @@ +import type { ToolConfig } from '@/tools/types' +import type { ProfoundQueryFanoutsParams, ProfoundQueryFanoutsResponse } from './types' + +export const profoundQueryFanoutsTool: ToolConfig< + ProfoundQueryFanoutsParams, + ProfoundQueryFanoutsResponse +> = { + id: 'profound_query_fanouts', + name: 'Profound Query Fanouts', + description: + 'Query fanout report showing how AI models expand prompts into sub-queries in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + categoryId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Category ID (UUID)', + }, + startDate: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Start date (YYYY-MM-DD or ISO 8601)', + }, + endDate: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'End date (YYYY-MM-DD or ISO 8601)', + }, + metrics: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Comma-separated metrics: fanouts_per_execution, total_fanouts, share', + }, + dimensions: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated dimensions: prompt, query, model, region, date', + }, + dateInterval: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Date interval: hour, day, week, month, year', + }, + filters: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'JSON array of filter objects', + }, + limit: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of results (default 10000, max 50000)', + }, + }, + + request: { + url: 'https://api.tryprofound.com/v1/reports/query-fanouts', + method: 'POST', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = { + category_id: params.categoryId, + start_date: params.startDate, + end_date: params.endDate, + metrics: params.metrics.split(',').map((m) => m.trim()), + } + if (params.dimensions) { + body.dimensions = params.dimensions.split(',').map((d) => d.trim()) + } + if (params.dateInterval) { + body.date_interval = params.dateInterval + } + if (params.filters) { + try { + body.filters = JSON.parse(params.filters) + } catch { + throw new Error('Invalid JSON in filters parameter') + } + } + if (params.limit != null) { + body.pagination = { limit: params.limit } + } + return body + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to query fanouts report') + } + return { + success: true, + output: { + totalRows: data.info?.total_rows ?? 0, + data: (data.data ?? []).map((row: { metrics: number[]; dimensions: string[] }) => ({ + metrics: row.metrics ?? [], + dimensions: row.dimensions ?? [], + })), + }, + } + }, + + outputs: { + totalRows: { + type: 'number', + description: 'Total number of rows in the report', + }, + data: { + type: 'json', + description: 'Report data rows with metrics and dimension values', + properties: { + metrics: { + type: 'json', + description: 'Array of metric values matching requested metrics order', + }, + dimensions: { + type: 'json', + description: 'Array of dimension values matching requested dimensions order', + }, + }, + }, + }, +} diff --git a/apps/sim/tools/profound/raw_logs.ts b/apps/sim/tools/profound/raw_logs.ts new file mode 100644 index 00000000000..93f65907887 --- /dev/null +++ b/apps/sim/tools/profound/raw_logs.ts @@ -0,0 +1,137 @@ +import type { ToolConfig } from '@/tools/types' +import type { ProfoundRawLogsParams, ProfoundRawLogsResponse } from './types' + +export const profoundRawLogsTool: ToolConfig = { + id: 'profound_raw_logs', + name: 'Profound Raw Logs', + description: 'Get raw traffic logs with filters for a domain in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + domain: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Domain to query logs for (e.g. example.com)', + }, + startDate: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Start date (YYYY-MM-DD or ISO 8601)', + }, + endDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'End date (YYYY-MM-DD or ISO 8601). Defaults to now', + }, + dimensions: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Comma-separated dimensions: timestamp, method, host, path, status_code, ip, user_agent, referer, bytes_sent, duration_ms, query_params', + }, + filters: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'JSON array of filter objects, e.g. [{"field":"path","operator":"contains","value":"/blog"}]', + }, + limit: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of results (default 10000, max 50000)', + }, + }, + + request: { + url: 'https://api.tryprofound.com/v1/logs/raw', + method: 'POST', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = { + domain: params.domain, + start_date: params.startDate, + metrics: ['count'], + } + if (params.endDate) { + body.end_date = params.endDate + } + if (params.dimensions) { + body.dimensions = params.dimensions.split(',').map((d) => d.trim()) + } + if (params.filters) { + try { + body.filters = JSON.parse(params.filters) + } catch { + throw new Error('Invalid JSON in filters parameter') + } + } + if (params.limit != null) { + body.pagination = { limit: params.limit } + } + return body + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to get raw logs') + } + if (Array.isArray(data)) { + return { + success: true, + output: { + totalRows: data.length, + data: data.map((row: { metrics: number[]; dimensions: string[] }) => ({ + metrics: row.metrics ?? [], + dimensions: row.dimensions ?? [], + })), + }, + } + } + return { + success: true, + output: { + totalRows: data.info?.total_rows ?? 0, + data: (data.data ?? []).map((row: { metrics: number[]; dimensions: string[] }) => ({ + metrics: row.metrics ?? [], + dimensions: row.dimensions ?? [], + })), + }, + } + }, + + outputs: { + totalRows: { + type: 'number', + description: 'Total number of log entries', + }, + data: { + type: 'json', + description: 'Log data rows with metrics and dimension values', + properties: { + metrics: { type: 'json', description: 'Array of metric values (count)' }, + dimensions: { + type: 'json', + description: 'Array of dimension values matching requested dimensions order', + }, + }, + }, + }, +} diff --git a/apps/sim/tools/profound/referrals_report.ts b/apps/sim/tools/profound/referrals_report.ts new file mode 100644 index 00000000000..b3036668620 --- /dev/null +++ b/apps/sim/tools/profound/referrals_report.ts @@ -0,0 +1,146 @@ +import type { ToolConfig } from '@/tools/types' +import type { ProfoundReferralsReportParams, ProfoundReferralsReportResponse } from './types' + +export const profoundReferralsReportTool: ToolConfig< + ProfoundReferralsReportParams, + ProfoundReferralsReportResponse +> = { + id: 'profound_referrals_report', + name: 'Profound Referrals Report', + description: + 'Query human referral traffic report with hourly granularity for a domain in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + domain: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Domain to query referral traffic for (e.g. example.com)', + }, + startDate: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Start date (YYYY-MM-DD or ISO 8601)', + }, + endDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'End date (YYYY-MM-DD or ISO 8601). Defaults to now', + }, + metrics: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Comma-separated metrics: visits, last_visit', + }, + dimensions: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated dimensions: date, hour, path, referral_source, referral_type', + }, + dateInterval: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Date interval: hour, day, week, month, year', + }, + filters: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'JSON array of filter objects, e.g. [{"field":"referral_source","operator":"is","value":"openai"}]', + }, + limit: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of results (default 10000, max 50000)', + }, + }, + + request: { + url: 'https://api.tryprofound.com/v2/reports/referrals', + method: 'POST', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = { + domain: params.domain, + start_date: params.startDate, + metrics: params.metrics.split(',').map((m) => m.trim()), + } + if (params.endDate) { + body.end_date = params.endDate + } + if (params.dimensions) { + body.dimensions = params.dimensions.split(',').map((d) => d.trim()) + } + if (params.dateInterval) { + body.date_interval = params.dateInterval + } + if (params.filters) { + try { + body.filters = JSON.parse(params.filters) + } catch { + throw new Error('Invalid JSON in filters parameter') + } + } + if (params.limit != null) { + body.pagination = { limit: params.limit } + } + return body + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to query referrals report') + } + return { + success: true, + output: { + totalRows: data.info?.total_rows ?? 0, + data: (data.data ?? []).map((row: { metrics: number[]; dimensions: string[] }) => ({ + metrics: row.metrics ?? [], + dimensions: row.dimensions ?? [], + })), + }, + } + }, + + outputs: { + totalRows: { + type: 'number', + description: 'Total number of rows in the report', + }, + data: { + type: 'json', + description: 'Report data rows with metrics and dimension values', + properties: { + metrics: { + type: 'json', + description: 'Array of metric values matching requested metrics order', + }, + dimensions: { + type: 'json', + description: 'Array of dimension values matching requested dimensions order', + }, + }, + }, + }, +} diff --git a/apps/sim/tools/profound/sentiment_report.ts b/apps/sim/tools/profound/sentiment_report.ts new file mode 100644 index 00000000000..54da5be8d31 --- /dev/null +++ b/apps/sim/tools/profound/sentiment_report.ts @@ -0,0 +1,144 @@ +import type { ToolConfig } from '@/tools/types' +import type { ProfoundSentimentReportParams, ProfoundSentimentReportResponse } from './types' + +export const profoundSentimentReportTool: ToolConfig< + ProfoundSentimentReportParams, + ProfoundSentimentReportResponse +> = { + id: 'profound_sentiment_report', + name: 'Profound Sentiment Report', + description: 'Query sentiment report for a category in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + categoryId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Category ID (UUID)', + }, + startDate: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Start date (YYYY-MM-DD or ISO 8601)', + }, + endDate: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'End date (YYYY-MM-DD or ISO 8601)', + }, + metrics: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Comma-separated metrics: positive, negative, occurrences', + }, + dimensions: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Comma-separated dimensions: theme, date, region, topic, model, asset_name, tag, prompt, sentiment_type, persona', + }, + dateInterval: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Date interval: hour, day, week, month, year', + }, + filters: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'JSON array of filter objects, e.g. [{"field":"asset_name","operator":"is","value":"Company"}]', + }, + limit: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of results (default 10000, max 50000)', + }, + }, + + request: { + url: 'https://api.tryprofound.com/v1/reports/sentiment', + method: 'POST', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = { + category_id: params.categoryId, + start_date: params.startDate, + end_date: params.endDate, + metrics: params.metrics.split(',').map((m) => m.trim()), + } + if (params.dimensions) { + body.dimensions = params.dimensions.split(',').map((d) => d.trim()) + } + if (params.dateInterval) { + body.date_interval = params.dateInterval + } + if (params.filters) { + try { + body.filters = JSON.parse(params.filters) + } catch { + throw new Error('Invalid JSON in filters parameter') + } + } + if (params.limit != null) { + body.pagination = { limit: params.limit } + } + return body + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to query sentiment report') + } + return { + success: true, + output: { + totalRows: data.info?.total_rows ?? 0, + data: (data.data ?? []).map((row: { metrics: number[]; dimensions: string[] }) => ({ + metrics: row.metrics ?? [], + dimensions: row.dimensions ?? [], + })), + }, + } + }, + + outputs: { + totalRows: { + type: 'number', + description: 'Total number of rows in the report', + }, + data: { + type: 'json', + description: 'Report data rows with metrics and dimension values', + properties: { + metrics: { + type: 'json', + description: 'Array of metric values matching requested metrics order', + }, + dimensions: { + type: 'json', + description: 'Array of dimension values matching requested dimensions order', + }, + }, + }, + }, +} diff --git a/apps/sim/tools/profound/types.ts b/apps/sim/tools/profound/types.ts new file mode 100644 index 00000000000..3ad94fec2fb --- /dev/null +++ b/apps/sim/tools/profound/types.ts @@ -0,0 +1,422 @@ +import type { ToolResponse } from '@/tools/types' + +/** Shared report response shape (visibility, sentiment, citations, bots, referrals, query fanouts, prompt volume) */ +export interface ProfoundReportResponse extends ToolResponse { + output: { + totalRows: number + data: Array<{ + metrics: number[] + dimensions: string[] + }> + } +} + +/** Shared report query params for category-based reports */ +export interface ProfoundCategoryReportParams { + apiKey: string + categoryId: string + startDate: string + endDate: string + metrics: string + dimensions?: string + dateInterval?: string + filters?: string + limit?: number +} + +/** Shared report query params for domain-based reports */ +export interface ProfoundDomainReportParams { + apiKey: string + domain: string + startDate: string + endDate?: string + metrics: string + dimensions?: string + dateInterval?: string + filters?: string + limit?: number +} + +// --- Organization endpoints --- + +export interface ProfoundListCategoriesParams { + apiKey: string +} + +export interface ProfoundListCategoriesResponse extends ToolResponse { + output: { + categories: Array<{ + id: string + name: string + }> + } +} + +export interface ProfoundListRegionsParams { + apiKey: string +} + +export interface ProfoundListRegionsResponse extends ToolResponse { + output: { + regions: Array<{ + id: string + name: string + }> + } +} + +export interface ProfoundListModelsParams { + apiKey: string +} + +export interface ProfoundListModelsResponse extends ToolResponse { + output: { + models: Array<{ + id: string + name: string + }> + } +} + +export interface ProfoundListDomainsParams { + apiKey: string +} + +export interface ProfoundListDomainsResponse extends ToolResponse { + output: { + domains: Array<{ + id: string + name: string + createdAt: string + }> + } +} + +export interface ProfoundListAssetsParams { + apiKey: string +} + +export interface ProfoundListAssetsResponse extends ToolResponse { + output: { + assets: Array<{ + id: string + name: string + website: string + alternateDomains: string[] | null + isOwned: boolean + createdAt: string + logoUrl: string + categoryId: string + categoryName: string + }> + } +} + +export interface ProfoundListPersonasParams { + apiKey: string +} + +export interface ProfoundListPersonasResponse extends ToolResponse { + output: { + personas: Array<{ + id: string + name: string + categoryId: string + categoryName: string + persona: { + behavior: { painPoints: string | null; motivations: string | null } + employment: { + industry: string[] + jobTitle: string[] + companySize: string[] + roleSeniority: string[] + } + demographics: { ageRange: string[] } + } + }> + } +} + +// --- Category-specific endpoints --- + +export interface ProfoundCategoryTopicsParams { + apiKey: string + categoryId: string +} + +export interface ProfoundCategoryTopicsResponse extends ToolResponse { + output: { + topics: Array<{ + id: string + name: string + }> + } +} + +export interface ProfoundCategoryTagsParams { + apiKey: string + categoryId: string +} + +export interface ProfoundCategoryTagsResponse extends ToolResponse { + output: { + tags: Array<{ + id: string + name: string + }> + } +} + +export interface ProfoundCategoryPromptsParams { + apiKey: string + categoryId: string + limit?: number + cursor?: string + orderDir?: string + promptType?: string + topicId?: string + tagId?: string + regionId?: string + platformId?: string +} + +export interface ProfoundCategoryPromptsResponse extends ToolResponse { + output: { + totalRows: number + nextCursor: string | null + prompts: Array<{ + id: string + prompt: string + promptType: string + topicId: string + topicName: string + tags: Array<{ id: string; name: string }> + regions: Array<{ id: string; name: string }> + platforms: Array<{ id: string; name: string }> + createdAt: string + }> + } +} + +export interface ProfoundCategoryAssetsParams { + apiKey: string + categoryId: string +} + +export interface ProfoundCategoryAssetsResponse extends ToolResponse { + output: { + assets: Array<{ + id: string + name: string + website: string + alternateDomains: string[] | null + isOwned: boolean + createdAt: string + logoUrl: string + }> + } +} + +export interface ProfoundCategoryPersonasParams { + apiKey: string + categoryId: string +} + +export interface ProfoundCategoryPersonasResponse extends ToolResponse { + output: { + personas: Array<{ + id: string + name: string + persona: { + behavior: { painPoints: string | null; motivations: string | null } + employment: { + industry: string[] + jobTitle: string[] + companySize: string[] + roleSeniority: string[] + } + demographics: { ageRange: string[] } + } + }> + } +} + +// --- Reports --- + +export type ProfoundVisibilityReportParams = ProfoundCategoryReportParams +export type ProfoundVisibilityReportResponse = ProfoundReportResponse + +export type ProfoundSentimentReportParams = ProfoundCategoryReportParams +export type ProfoundSentimentReportResponse = ProfoundReportResponse + +export type ProfoundCitationsReportParams = ProfoundCategoryReportParams +export type ProfoundCitationsReportResponse = ProfoundReportResponse + +export type ProfoundQueryFanoutsParams = ProfoundCategoryReportParams +export type ProfoundQueryFanoutsResponse = ProfoundReportResponse + +export type ProfoundBotsReportParams = ProfoundDomainReportParams +export type ProfoundBotsReportResponse = ProfoundReportResponse + +export type ProfoundReferralsReportParams = ProfoundDomainReportParams +export type ProfoundReferralsReportResponse = ProfoundReportResponse + +// --- Prompts --- + +export interface ProfoundPromptAnswersParams { + apiKey: string + categoryId: string + startDate: string + endDate: string + filters?: string + limit?: number +} + +export interface ProfoundPromptAnswersResponse extends ToolResponse { + output: { + totalRows: number + data: Array<{ + prompt: string | null + promptType: string | null + response: string | null + mentions: string[] | null + citations: string[] | null + topic: string | null + region: string | null + model: string | null + asset: string | null + createdAt: string | null + }> + } +} + +// --- Agent Analytics --- + +export interface ProfoundRawLogsParams { + apiKey: string + domain: string + startDate: string + endDate?: string + dimensions?: string + filters?: string + limit?: number +} + +export interface ProfoundRawLogsResponse extends ToolResponse { + output: { + totalRows: number + data: Array<{ + metrics: number[] + dimensions: string[] + }> + } +} + +export interface ProfoundBotLogsParams { + apiKey: string + domain: string + startDate: string + endDate?: string + dimensions?: string + filters?: string + limit?: number +} + +export interface ProfoundBotLogsResponse extends ToolResponse { + output: { + totalRows: number + data: Array<{ + metrics: number[] + dimensions: string[] + }> + } +} + +// --- Content --- + +export interface ProfoundListOptimizationsParams { + apiKey: string + assetId: string + limit?: number + offset?: number +} + +export interface ProfoundListOptimizationsResponse extends ToolResponse { + output: { + totalRows: number + optimizations: Array<{ + id: string + title: string + createdAt: string + extractedInput: string | null + type: string + status: string + }> + } +} + +export interface ProfoundOptimizationAnalysisParams { + apiKey: string + assetId: string + contentId: string +} + +export interface ProfoundOptimizationAnalysisResponse extends ToolResponse { + output: { + content: { + format: string + value: string + } + aeoContentScore: { + value: number + targetZone: { low: number; high: number } + } | null + analysis: { + breakdown: Array<{ + title: string + weight: number + score: number + }> + } + recommendations: Array<{ + title: string + status: string + impact: { section: string; score: number } | null + suggestion: { text: string; rationale: string } + }> + } +} + +// --- Prompt Volumes --- + +export interface ProfoundPromptVolumeParams { + apiKey: string + startDate: string + endDate: string + metrics: string + dimensions?: string + dateInterval?: string + filters?: string + limit?: number +} + +export interface ProfoundPromptVolumeResponse extends ToolResponse { + output: { + totalRows: number + data: Array<{ + metrics: number[] + dimensions: string[] + }> + } +} + +export interface ProfoundCitationPromptsParams { + apiKey: string + inputDomain: string +} + +export interface ProfoundCitationPromptsResponse extends ToolResponse { + output: { + data: unknown + } +} diff --git a/apps/sim/tools/profound/visibility_report.ts b/apps/sim/tools/profound/visibility_report.ts new file mode 100644 index 00000000000..b7fdd91dd79 --- /dev/null +++ b/apps/sim/tools/profound/visibility_report.ts @@ -0,0 +1,145 @@ +import type { ToolConfig } from '@/tools/types' +import type { ProfoundVisibilityReportParams, ProfoundVisibilityReportResponse } from './types' + +export const profoundVisibilityReportTool: ToolConfig< + ProfoundVisibilityReportParams, + ProfoundVisibilityReportResponse +> = { + id: 'profound_visibility_report', + name: 'Profound Visibility Report', + description: 'Query AI visibility report for a category in Profound', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Profound API Key', + }, + categoryId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Category ID (UUID)', + }, + startDate: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Start date (YYYY-MM-DD or ISO 8601)', + }, + endDate: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'End date (YYYY-MM-DD or ISO 8601)', + }, + metrics: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: + 'Comma-separated metrics: share_of_voice, mentions_count, visibility_score, executions, average_position', + }, + dimensions: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Comma-separated dimensions: date, region, topic, model, asset_name, prompt, tag, persona', + }, + dateInterval: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Date interval: hour, day, week, month, year', + }, + filters: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'JSON array of filter objects, e.g. [{"field":"asset_name","operator":"is","value":"Company"}]', + }, + limit: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of results (default 10000, max 50000)', + }, + }, + + request: { + url: 'https://api.tryprofound.com/v1/reports/visibility', + method: 'POST', + headers: (params) => ({ + 'X-API-Key': params.apiKey, + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = { + category_id: params.categoryId, + start_date: params.startDate, + end_date: params.endDate, + metrics: params.metrics.split(',').map((m) => m.trim()), + } + if (params.dimensions) { + body.dimensions = params.dimensions.split(',').map((d) => d.trim()) + } + if (params.dateInterval) { + body.date_interval = params.dateInterval + } + if (params.filters) { + try { + body.filters = JSON.parse(params.filters) + } catch { + throw new Error('Invalid JSON in filters parameter') + } + } + if (params.limit != null) { + body.pagination = { limit: params.limit } + } + return body + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.detail?.[0]?.msg || 'Failed to query visibility report') + } + return { + success: true, + output: { + totalRows: data.info?.total_rows ?? 0, + data: (data.data ?? []).map((row: { metrics: number[]; dimensions: string[] }) => ({ + metrics: row.metrics ?? [], + dimensions: row.dimensions ?? [], + })), + }, + } + }, + + outputs: { + totalRows: { + type: 'number', + description: 'Total number of rows in the report', + }, + data: { + type: 'json', + description: 'Report data rows with metrics and dimension values', + properties: { + metrics: { + type: 'json', + description: 'Array of metric values matching requested metrics order', + }, + dimensions: { + type: 'json', + description: 'Array of dimension values matching requested dimensions order', + }, + }, + }, + }, +} diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index 66d70a07f7b..5e1219d006c 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -1771,6 +1771,32 @@ import { posthogUpdatePropertyDefinitionTool, posthogUpdateSurveyTool, } from '@/tools/posthog' +import { + profoundBotLogsTool, + profoundBotsReportTool, + profoundCategoryAssetsTool, + profoundCategoryPersonasTool, + profoundCategoryPromptsTool, + profoundCategoryTagsTool, + profoundCategoryTopicsTool, + profoundCitationPromptsTool, + profoundCitationsReportTool, + profoundListAssetsTool, + profoundListCategoriesTool, + profoundListDomainsTool, + profoundListModelsTool, + profoundListOptimizationsTool, + profoundListPersonasTool, + profoundListRegionsTool, + profoundOptimizationAnalysisTool, + profoundPromptAnswersTool, + profoundPromptVolumeTool, + profoundQueryFanoutsTool, + profoundRawLogsTool, + profoundReferralsReportTool, + profoundSentimentReportTool, + profoundVisibilityReportTool, +} from '@/tools/profound' import { pulseParserTool, pulseParserV2Tool } from '@/tools/pulse' import { qdrantFetchTool, qdrantSearchTool, qdrantUpsertTool } from '@/tools/qdrant' import { quiverImageToSvgTool, quiverListModelsTool, quiverTextToSvgTool } from '@/tools/quiver' @@ -3623,6 +3649,30 @@ export const tools: Record = { google_slides_insert_text: googleSlidesInsertTextTool, perplexity_chat: perplexityChatTool, perplexity_search: perplexitySearchTool, + profound_bot_logs: profoundBotLogsTool, + profound_bots_report: profoundBotsReportTool, + profound_category_assets: profoundCategoryAssetsTool, + profound_category_personas: profoundCategoryPersonasTool, + profound_category_prompts: profoundCategoryPromptsTool, + profound_category_tags: profoundCategoryTagsTool, + profound_category_topics: profoundCategoryTopicsTool, + profound_citation_prompts: profoundCitationPromptsTool, + profound_citations_report: profoundCitationsReportTool, + profound_list_assets: profoundListAssetsTool, + profound_list_categories: profoundListCategoriesTool, + profound_list_domains: profoundListDomainsTool, + profound_list_models: profoundListModelsTool, + profound_list_optimizations: profoundListOptimizationsTool, + profound_list_personas: profoundListPersonasTool, + profound_list_regions: profoundListRegionsTool, + profound_optimization_analysis: profoundOptimizationAnalysisTool, + profound_prompt_answers: profoundPromptAnswersTool, + profound_prompt_volume: profoundPromptVolumeTool, + profound_query_fanouts: profoundQueryFanoutsTool, + profound_raw_logs: profoundRawLogsTool, + profound_referrals_report: profoundReferralsReportTool, + profound_sentiment_report: profoundSentimentReportTool, + profound_visibility_report: profoundVisibilityReportTool, pulse_parser: pulseParserTool, pulse_parser_v2: pulseParserV2Tool, quiver_image_to_svg: quiverImageToSvgTool, diff --git a/scripts/generate-docs.ts b/scripts/generate-docs.ts index 9f6b4387fa8..13a1c509dfa 100755 --- a/scripts/generate-docs.ts +++ b/scripts/generate-docs.ts @@ -205,12 +205,27 @@ async function generateIconMapping(): Promise> { * Write the icon mapping to the docs app * This file is imported by BlockInfoCard to resolve icons automatically */ +/** + * Sort strings to match Biome's organizeImports order: + * case-insensitive character-by-character, uppercase before lowercase as tiebreaker. + */ +function biomeSortCompare(a: string, b: string): number { + const minLen = Math.min(a.length, b.length) + for (let i = 0; i < minLen; i++) { + const al = a[i].toLowerCase() + const bl = b[i].toLowerCase() + if (al !== bl) return al < bl ? -1 : 1 + if (a[i] !== b[i]) return a[i] < b[i] ? -1 : 1 + } + return a.length - b.length +} + function writeIconMapping(iconMapping: Record): void { try { const iconMappingPath = path.join(rootDir, 'apps/docs/components/ui/icon-mapping.ts') - // Get unique icon names - const iconNames = [...new Set(Object.values(iconMapping))].sort() + // Get unique icon names, sorted to match Biome's organizeImports + const iconNames = [...new Set(Object.values(iconMapping))].sort(biomeSortCompare) // Generate imports const imports = iconNames.map((icon) => ` ${icon},`).join('\n') @@ -508,7 +523,7 @@ function writeIntegrationsIconMapping(iconMapping: Record): void } const iconMappingPath = path.join(LANDING_INTEGRATIONS_DATA_PATH, 'icon-mapping.ts') - const iconNames = [...new Set(Object.values(iconMapping))].sort() + const iconNames = [...new Set(Object.values(iconMapping))].sort(biomeSortCompare) const imports = iconNames.map((icon) => ` ${icon},`).join('\n') const mappingEntries = Object.entries(iconMapping) .sort(([a], [b]) => a.localeCompare(b)) @@ -664,7 +679,16 @@ async function writeIntegrationsJson(iconMapping: Record): Promi integrations.sort((a, b) => a.name.localeCompare(b.name)) const jsonPath = path.join(LANDING_INTEGRATIONS_DATA_PATH, 'integrations.json') - fs.writeFileSync(jsonPath, JSON.stringify(integrations, null, 2)) + // JSON.stringify always expands arrays across multiple lines. Biome's formatter + // collapses short arrays of primitives onto single lines. Post-process to match. + const json = JSON.stringify(integrations, null, 2).replace( + /\[\n(\s+"[^"\n]*"(?:,\n\s+"[^"\n]*")*)\n\s+\]/g, + (_match, inner) => { + const items = (inner as string).split(',\n').map((s: string) => s.trim()) + return `[${items.join(', ')}]` + } + ) + fs.writeFileSync(jsonPath, `${json}\n`) console.log(`✓ Integration data written: ${integrations.length} integrations → ${jsonPath}`) } catch (error) { console.error('Error writing integrations JSON:', error) @@ -2813,7 +2837,7 @@ function updateMetaJson() { pages: items, } - fs.writeFileSync(metaJsonPath, JSON.stringify(metaJson, null, 2)) + fs.writeFileSync(metaJsonPath, `${JSON.stringify(metaJson, null, 2)}\n`) console.log(`Updated meta.json with ${items.length} entries`) }