diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx index 1ebe7b408bc..331488447d5 100644 --- a/apps/docs/components/icons.tsx +++ b/apps/docs/components/icons.tsx @@ -1,6 +1,46 @@ import type { SVGProps } from 'react' import { useId } from 'react' +export function AdanosIcon(props: SVGProps) { + const gradientId = useId() + + return ( + + + + + + + + + + + + + + + ) +} + export function EnrichmentIcon(props: SVGProps) { return ( > export const blockTypeToIconMap: Record = { + adanos: AdanosIcon, agentmail: AgentMailIcon, agentphone: AgentPhoneIcon, agiloft: AgiloftIcon, diff --git a/apps/docs/content/docs/en/integrations/adanos.mdx b/apps/docs/content/docs/en/integrations/adanos.mdx new file mode 100644 index 00000000000..eeb5d2df0e6 --- /dev/null +++ b/apps/docs/content/docs/en/integrations/adanos.mdx @@ -0,0 +1,154 @@ +--- +title: Adanos +description: Use stock and crypto market sentiment in workflows +--- + +import { BlockInfoCard } from "@/components/ui/block-info-card" + + + +{/* MANUAL-CONTENT-START:intro */} +[Adanos](https://adanos.org/) provides market sentiment data for stocks and cryptocurrencies. The integration can retrieve source-specific stock signals from Reddit, X / FinTwit, financial news, and Polymarket, plus Reddit crypto sentiment. + +Create an API key through the [Adanos API documentation](https://api.adanos.org/docs) and enter it in the block. Each workflow uses the supplied key, so requests remain subject to that Adanos account's plan and usage limits. + +Stock sources are queried independently. Mention counts from social and news sources and trade counts from Polymarket are normalized as `activityCount`, but should be interpreted in their original source context. +{/* MANUAL-CONTENT-END */} + +## Usage Instructions + +Retrieve market sentiment, trending assets, and market-wide signals for stocks and cryptocurrencies from Reddit, X / FinTwit, financial news, and Polymarket. + + + +## Actions + +### `adanos_stock_sentiment` + +Get stock sentiment from Reddit, X / FinTwit, news, or Polymarket + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Adanos API key | +| `ticker` | string | Yes | US stock ticker symbol, such as AAPL | +| `source` | string | Yes | Sentiment source: reddit, x, news, or polymarket | +| `startDate` | string | No | Start date in YYYY-MM-DD format | +| `endDate` | string | No | End date in YYYY-MM-DD format | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `assetType` | string | Asset type | +| `source` | string | Sentiment data source | +| `symbol` | string | Stock ticker symbol | +| `name` | string | Company name | +| `found` | boolean | Whether sentiment data was found | +| `buzzScore` | number | Buzz score from 0 to 100 | +| `sentimentScore` | number | Sentiment score from -1 to 1 | +| `trend` | string | Current sentiment trend | +| `bullishPct` | number | Bullish activity percentage | +| `bearishPct` | number | Bearish activity percentage | +| `activityCount` | number | Mention or trade count | +| `periodDays` | number | Number of days in the period | +| `dailyTrend` | json | Normalized daily sentiment trend | + +### `adanos_crypto_sentiment` + +Get Reddit market sentiment for a cryptocurrency + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Adanos API key | +| `symbol` | string | Yes | Cryptocurrency symbol, such as BTC | +| `startDate` | string | No | Start date in YYYY-MM-DD format | +| `endDate` | string | No | End date in YYYY-MM-DD format | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `assetType` | string | Asset type | +| `source` | string | Sentiment data source | +| `symbol` | string | Cryptocurrency symbol | +| `name` | string | Cryptocurrency name | +| `found` | boolean | Whether sentiment data was found | +| `buzzScore` | number | Buzz score from 0 to 100 | +| `sentimentScore` | number | Sentiment score from -1 to 1 | +| `trend` | string | Current sentiment trend | +| `bullishPct` | number | Bullish mention percentage | +| `bearishPct` | number | Bearish mention percentage | +| `activityCount` | number | Reddit mention count | +| `periodDays` | number | Number of days in the period | +| `dailyTrend` | json | Normalized daily sentiment trend | + +### `adanos_trending` + +List trending stocks or cryptocurrencies ranked by market sentiment activity + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Adanos API key | +| `assetType` | string | Yes | Asset type: stocks or crypto | +| `source` | string | No | Stock sentiment source: reddit, x, news, or polymarket | +| `limit` | number | No | Maximum number of assets to return, from 1 to 100 | +| `startDate` | string | No | Start date in YYYY-MM-DD format | +| `endDate` | string | No | End date in YYYY-MM-DD format | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `assets` | array | Trending assets with normalized sentiment metrics | +| ↳ `assetType` | string | Asset type | +| ↳ `source` | string | Sentiment data source | +| ↳ `symbol` | string | Ticker or cryptocurrency symbol | +| ↳ `name` | string | Asset name | +| ↳ `buzzScore` | number | Buzz score from 0 to 100 | +| ↳ `sentimentScore` | number | Sentiment score from -1 to 1 | +| ↳ `trend` | string | Current sentiment trend | +| ↳ `bullishPct` | number | Bullish activity percentage | +| ↳ `bearishPct` | number | Bearish activity percentage | +| ↳ `activityCount` | number | Mention or trade count | + +### `adanos_market_sentiment` + +Get aggregate market sentiment for stocks or cryptocurrencies + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Adanos API key | +| `assetType` | string | Yes | Asset type: stocks or crypto | +| `source` | string | No | Stock sentiment source: reddit, x, news, or polymarket | +| `startDate` | string | No | Start date in YYYY-MM-DD format | +| `endDate` | string | No | End date in YYYY-MM-DD format | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `assetType` | string | Asset type | +| `source` | string | Sentiment data source | +| `buzzScore` | number | Market buzz score from 0 to 100 | +| `sentimentScore` | number | Aggregate sentiment score from -1 to 1 | +| `trend` | string | Current market sentiment trend | +| `bullishPct` | number | Bullish activity percentage | +| `bearishPct` | number | Bearish activity percentage | +| `activityCount` | number | Aggregate mention or trade count | +| `activeAssets` | number | Number of active assets | +| `drivers` | array | Assets contributing most to market sentiment | +| ↳ `symbol` | string | Ticker or cryptocurrency symbol | +| ↳ `activityCount` | number | Mention or trade count | +| ↳ `buzzScore` | number | Buzz score from 0 to 100 | +| ↳ `sentimentScore` | number | Sentiment score from -1 to 1 | diff --git a/apps/docs/content/docs/en/integrations/meta.json b/apps/docs/content/docs/en/integrations/meta.json index 551b8d25890..6b3227fdfe2 100644 --- a/apps/docs/content/docs/en/integrations/meta.json +++ b/apps/docs/content/docs/en/integrations/meta.json @@ -22,6 +22,7 @@ "zoom-service-account", "---All Integrations---", "a2a", + "adanos", "agentmail", "agentphone", "agiloft", diff --git a/apps/sim/blocks/blocks/adanos.ts b/apps/sim/blocks/blocks/adanos.ts new file mode 100644 index 00000000000..beaa15c7d33 --- /dev/null +++ b/apps/sim/blocks/blocks/adanos.ts @@ -0,0 +1,260 @@ +import { AdanosIcon } from '@/components/icons' +import type { BlockConfig, BlockMeta } from '@/blocks/types' +import { AuthMode, IntegrationType } from '@/blocks/types' + +const dateWandConfig = { + enabled: true, + prompt: + 'Generate a date in YYYY-MM-DD format from the user description. Return only the date string.', + placeholder: 'Describe a date, such as "30 days ago"', + generationType: 'timestamp' as const, +} + +export const AdanosBlock: BlockConfig = { + type: 'adanos', + name: 'Adanos', + description: 'Use stock and crypto market sentiment in workflows', + authMode: AuthMode.ApiKey, + longDescription: + 'Retrieve market sentiment, trending assets, and market-wide signals for stocks and cryptocurrencies from Reddit, X / FinTwit, financial news, and Polymarket.', + docsLink: 'https://docs.sim.ai/integrations/adanos', + category: 'tools', + integrationType: IntegrationType.Analytics, + bgColor: '#FFFFFF', + icon: AdanosIcon, + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + options: [ + { label: 'Get Stock Sentiment', id: 'stock_sentiment' }, + { label: 'Get Crypto Sentiment', id: 'crypto_sentiment' }, + { label: 'List Trending Assets', id: 'trending' }, + { label: 'Get Market Sentiment', id: 'market_sentiment' }, + ], + value: () => 'stock_sentiment', + }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + password: true, + placeholder: 'Enter your Adanos API key', + required: true, + }, + { + id: 'ticker', + title: 'Ticker', + type: 'short-input', + placeholder: 'AAPL', + condition: { field: 'operation', value: 'stock_sentiment' }, + required: { field: 'operation', value: 'stock_sentiment' }, + }, + { + id: 'symbol', + title: 'Crypto Symbol', + type: 'short-input', + placeholder: 'BTC', + condition: { field: 'operation', value: 'crypto_sentiment' }, + required: { field: 'operation', value: 'crypto_sentiment' }, + }, + { + id: 'assetType', + title: 'Asset Type', + type: 'dropdown', + options: [ + { label: 'Stocks', id: 'stocks' }, + { label: 'Crypto', id: 'crypto' }, + ], + value: () => 'stocks', + condition: { field: 'operation', value: ['trending', 'market_sentiment'] }, + required: { field: 'operation', value: ['trending', 'market_sentiment'] }, + }, + { + id: 'source', + title: 'Stock Source', + type: 'dropdown', + options: [ + { label: 'Reddit', id: 'reddit' }, + { label: 'X / FinTwit', id: 'x' }, + { label: 'Financial News', id: 'news' }, + { label: 'Polymarket', id: 'polymarket' }, + ], + value: () => 'reddit', + condition: { + field: 'operation', + value: ['stock_sentiment', 'trending', 'market_sentiment'], + and: { field: 'assetType', value: 'crypto', not: true }, + }, + }, + { + id: 'limit', + title: 'Result Limit', + type: 'short-input', + placeholder: '1-100', + condition: { field: 'operation', value: 'trending' }, + mode: 'advanced', + }, + { + id: 'startDate', + title: 'Start Date', + type: 'short-input', + placeholder: 'YYYY-MM-DD', + mode: 'advanced', + wandConfig: dateWandConfig, + }, + { + id: 'endDate', + title: 'End Date', + type: 'short-input', + placeholder: 'YYYY-MM-DD', + mode: 'advanced', + wandConfig: dateWandConfig, + }, + ], + tools: { + access: [ + 'adanos_stock_sentiment', + 'adanos_crypto_sentiment', + 'adanos_trending', + 'adanos_market_sentiment', + ], + config: { + tool: (params) => `adanos_${params.operation}`, + params: (params) => { + const { operation, ...rest } = params + const parsedLimit = params.limit ? Number.parseInt(params.limit, 10) : undefined + + return { + ...rest, + source: params.source || undefined, + startDate: params.startDate || undefined, + endDate: params.endDate || undefined, + limit: parsedLimit !== undefined && !Number.isNaN(parsedLimit) ? parsedLimit : undefined, + } + }, + }, + }, + inputs: { + operation: { type: 'string', description: 'Operation to perform' }, + apiKey: { type: 'string', description: 'Adanos API key' }, + ticker: { type: 'string', description: 'US stock ticker symbol' }, + symbol: { type: 'string', description: 'Cryptocurrency symbol' }, + assetType: { type: 'string', description: 'Asset type: stocks or crypto' }, + source: { type: 'string', description: 'Stock sentiment source' }, + limit: { type: 'string', description: 'Maximum number of trending assets' }, + startDate: { type: 'string', description: 'Start date in YYYY-MM-DD format' }, + endDate: { type: 'string', description: 'End date in YYYY-MM-DD format' }, + }, + outputs: { + assetType: { type: 'string', description: 'Asset type' }, + source: { type: 'string', description: 'Sentiment data source' }, + symbol: { type: 'string', description: 'Ticker or cryptocurrency symbol' }, + name: { type: 'string', description: 'Asset name' }, + found: { type: 'boolean', description: 'Whether sentiment data was found' }, + buzzScore: { type: 'number', description: 'Buzz score from 0 to 100' }, + sentimentScore: { type: 'number', description: 'Sentiment score from -1 to 1' }, + trend: { type: 'string', description: 'Current sentiment trend' }, + bullishPct: { type: 'number', description: 'Bullish activity percentage' }, + bearishPct: { type: 'number', description: 'Bearish activity percentage' }, + activityCount: { type: 'number', description: 'Mention or trade count' }, + periodDays: { type: 'number', description: 'Number of days in the period' }, + dailyTrend: { type: 'json', description: 'Normalized daily trend' }, + assets: { type: 'json', description: 'Trending assets' }, + activeAssets: { type: 'number', description: 'Number of active assets' }, + drivers: { type: 'json', description: 'Top market sentiment drivers' }, + }, +} + +export const AdanosBlockMeta = { + tags: ['data-analytics', 'prediction-markets', 'web-scraping'], + url: 'https://adanos.org', + templates: [ + { + icon: AdanosIcon, + title: 'Daily stock sentiment brief', + prompt: + 'Build a scheduled workflow that compares Reddit, X, news, and Polymarket sentiment for a watchlist and emails a concise daily brief.', + modules: ['scheduled', 'agent', 'workflows'], + category: 'productivity', + tags: ['data-analytics', 'automation'], + alsoIntegrations: ['gmail'], + }, + { + icon: AdanosIcon, + title: 'Sentiment divergence monitor', + prompt: + 'Create a workflow that checks a stock across all Adanos sources and alerts when social sentiment diverges materially from news or prediction markets.', + modules: ['scheduled', 'agent', 'workflows'], + category: 'operations', + tags: ['monitoring', 'automation'], + }, + { + icon: AdanosIcon, + title: 'Trending stock research queue', + prompt: + 'Build a workflow that fetches trending stocks from Adanos, ranks them by buzz and sentiment, and writes the shortlist to a research table.', + modules: ['tables', 'agent', 'workflows'], + category: 'productivity', + tags: ['data-analytics', 'research'], + }, + { + icon: AdanosIcon, + title: 'Crypto sentiment digest', + prompt: + 'Create a scheduled workflow that summarizes Reddit sentiment for BTC, ETH, and trending cryptocurrencies and posts the digest to Slack.', + modules: ['scheduled', 'agent', 'workflows'], + category: 'productivity', + tags: ['automation', 'data-analytics'], + alsoIntegrations: ['slack'], + }, + { + icon: AdanosIcon, + title: 'Market mood dashboard feed', + prompt: + 'Build a workflow that records aggregate stock market sentiment and top drivers from Adanos in a table for dashboarding and historical comparison.', + modules: ['scheduled', 'tables', 'workflows'], + category: 'operations', + tags: ['data-analytics', 'sync'], + }, + { + icon: AdanosIcon, + title: 'Pre-earnings sentiment check', + prompt: + 'Create an agent workflow that collects recent news, social, and Polymarket sentiment for a ticker before an earnings event and produces a sourced research note.', + modules: ['agent', 'files', 'workflows'], + category: 'productivity', + tags: ['research', 'content'], + }, + { + icon: AdanosIcon, + title: 'Portfolio sentiment watch', + prompt: + 'Build a scheduled workflow that checks each portfolio ticker with Adanos and sends an alert when sentiment trend or buzz crosses configured thresholds.', + modules: ['scheduled', 'tables', 'workflows'], + category: 'operations', + tags: ['monitoring', 'automation'], + }, + ], + skills: [ + { + name: 'compare-stock-sentiment-sources', + description: 'Compare source-specific sentiment signals for a stock without conflating them.', + content: + '# Compare Stock Sentiment Sources\n\nUse Adanos to compare distinct market narratives for one stock.\n\n## Steps\n1. Select the ticker and a consistent date range.\n2. Run Get Stock Sentiment once for each relevant source: Reddit, X / FinTwit, Financial News, and Polymarket.\n3. Compare sentiment score, buzz score, bullish and bearish percentages, trend, and activity count.\n4. Treat mention counts and Polymarket trade counts as source-specific activity measures rather than directly interchangeable volume.\n5. Highlight agreement, divergence, missing data, and the time period used.\n\n## Output\nA compact comparison table followed by the strongest agreements and divergences. Do not turn sentiment into investment advice.', + }, + { + name: 'find-trending-assets', + description: 'Find trending stocks or cryptocurrencies and prioritize them for research.', + content: + '# Find Trending Assets\n\nUse Adanos trending data to create a research queue.\n\n## Steps\n1. Choose stocks or crypto. For stocks, choose the source whose audience best matches the research goal.\n2. Set an appropriate result limit and optional date range.\n3. Run List Trending Assets.\n4. Rank by buzz score, then use sentiment score, trend, and activity count as context.\n5. Remove assets with missing symbols and note sparse metrics.\n\n## Output\nA ranked shortlist with symbol, source, buzz, sentiment, trend, and activity. Explain why each item deserves follow-up research.', + }, + { + name: 'summarize-market-sentiment', + description: 'Summarize aggregate market mood and the assets driving it.', + content: + '# Summarize Market Sentiment\n\nUse Adanos market sentiment to report the current market mood.\n\n## Steps\n1. Choose stocks or crypto and a date range. For stocks, select Reddit, X, news, or Polymarket.\n2. Run Get Market Sentiment.\n3. Report sentiment score, buzz score, bullish and bearish percentages, trend, and aggregate activity.\n4. List the top drivers with their activity, buzz, and sentiment metrics.\n5. State the source and period prominently so the result is not mistaken for a blended all-source index.\n\n## Output\nA short market-mood summary, a driver table, and a caveat that sentiment is a research signal rather than a forecast.', + }, + ], +} as const satisfies BlockMeta diff --git a/apps/sim/blocks/registry-maps.ts b/apps/sim/blocks/registry-maps.ts index 65a48b4c310..20e20c7da57 100644 --- a/apps/sim/blocks/registry-maps.ts +++ b/apps/sim/blocks/registry-maps.ts @@ -1,4 +1,5 @@ import { A2ABlock } from '@/blocks/blocks/a2a' +import { AdanosBlock, AdanosBlockMeta } from '@/blocks/blocks/adanos' import { AgentBlock } from '@/blocks/blocks/agent' import { AgentMailBlock, AgentMailBlockMeta } from '@/blocks/blocks/agentmail' import { AgentPhoneBlock, AgentPhoneBlockMeta } from '@/blocks/blocks/agentphone' @@ -342,6 +343,7 @@ import type { BlockConfig, BlockMeta } from '@/blocks/types' /** All block configs keyed by block type. The execution source of truth. */ export const BLOCK_REGISTRY: Record = { a2a: A2ABlock, + adanos: AdanosBlock, agent: AgentBlock, agentmail: AgentMailBlock, agentphone: AgentPhoneBlock, @@ -669,6 +671,7 @@ export const BLOCK_REGISTRY: Record = { * index). The toolbar and search modal read block *configs*, not metas. */ export const BLOCK_META_REGISTRY: Record = { + adanos: AdanosBlockMeta, agentmail: AgentMailBlockMeta, agentphone: AgentPhoneBlockMeta, agiloft: AgiloftBlockMeta, diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index 9ac49e147a7..9438126e6cf 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -1,6 +1,46 @@ import type { SVGProps } from 'react' import { useId } from 'react' +export function AdanosIcon(props: SVGProps) { + const gradientId = useId() + + return ( + + + + + + + + + + + + + + + ) +} + export function EnrichmentIcon(props: SVGProps) { return ( > export const blockTypeToIconMap: Record = { + adanos: AdanosIcon, agentmail: AgentMailIcon, agentphone: AgentPhoneIcon, agiloft: AgiloftIcon, diff --git a/apps/sim/lib/integrations/integrations.json b/apps/sim/lib/integrations/integrations.json index de2f74c9522..0ad37789069 100644 --- a/apps/sim/lib/integrations/integrations.json +++ b/apps/sim/lib/integrations/integrations.json @@ -1,5 +1,5 @@ { - "updatedAt": "2026-07-16", + "updatedAt": "2026-07-19", "integrations": [ { "type": "onepassword", @@ -60,6 +60,41 @@ "integrationType": "security", "tags": ["secrets-management", "identity"] }, + { + "type": "adanos", + "slug": "adanos", + "name": "Adanos", + "description": "Use stock and crypto market sentiment in workflows", + "longDescription": "Retrieve market sentiment, trending assets, and market-wide signals for stocks and cryptocurrencies from Reddit, X / FinTwit, financial news, and Polymarket.", + "bgColor": "#FFFFFF", + "iconName": "AdanosIcon", + "docsUrl": "https://docs.sim.ai/integrations/adanos", + "operations": [ + { + "name": "Get Stock Sentiment", + "description": "Get stock sentiment from Reddit, X / FinTwit, news, or Polymarket" + }, + { + "name": "Get Crypto Sentiment", + "description": "Get Reddit market sentiment for a cryptocurrency" + }, + { + "name": "List Trending Assets", + "description": "List trending stocks or cryptocurrencies ranked by market sentiment activity" + }, + { + "name": "Get Market Sentiment", + "description": "Get aggregate market sentiment for stocks or cryptocurrencies" + } + ], + "operationCount": 4, + "triggers": [], + "triggerCount": 0, + "authType": "api-key", + "category": "tools", + "integrationType": "analytics", + "tags": ["data-analytics", "prediction-markets", "web-scraping"] + }, { "type": "agentmail", "slug": "agentmail", diff --git a/apps/sim/tools/adanos/adanos.test.ts b/apps/sim/tools/adanos/adanos.test.ts new file mode 100644 index 00000000000..1eddb2aba53 --- /dev/null +++ b/apps/sim/tools/adanos/adanos.test.ts @@ -0,0 +1,225 @@ +/** + * @vitest-environment node + */ +import { describe, expect, it } from 'vitest' +import { AdanosBlock } from '@/blocks/blocks/adanos' +import { adanosCryptoSentimentTool } from '@/tools/adanos/crypto_sentiment' +import { adanosMarketSentimentTool } from '@/tools/adanos/market_sentiment' +import { adanosStockSentimentTool } from '@/tools/adanos/stock_sentiment' +import { adanosTrendingTool } from '@/tools/adanos/trending' + +const respond = (body: unknown, status = 200) => + new Response(JSON.stringify(body), { + status, + headers: { 'Content-Type': 'application/json' }, + }) + +describe('Adanos request configuration', () => { + it('builds a stock source URL with an uppercase ticker and date range', () => { + const buildUrl = adanosStockSentimentTool.request.url as (params: { + apiKey: string + ticker: string + source: 'news' + startDate: string + endDate: string + }) => string + + expect( + buildUrl({ + apiKey: 'key', + ticker: ' aapl ', + source: 'news', + startDate: '2026-07-01', + endDate: '2026-07-18', + }) + ).toBe('https://api.adanos.org/news/stocks/v1/stock/AAPL?from=2026-07-01&to=2026-07-18') + }) + + it('uses the Reddit crypto endpoint and trims the API key', () => { + const buildUrl = adanosCryptoSentimentTool.request.url as (params: { + apiKey: string + symbol: string + }) => string + const buildHeaders = adanosCryptoSentimentTool.request.headers as (params: { + apiKey: string + }) => Record + + expect(buildUrl({ apiKey: 'key', symbol: ' eth ' })).toBe( + 'https://api.adanos.org/reddit/crypto/v1/token/ETH' + ) + expect(buildHeaders({ apiKey: ' sk_live_test ' })).toMatchObject({ + 'X-API-Key': 'sk_live_test', + }) + }) + + it('builds a crypto trending URL with a bounded result limit', () => { + const buildUrl = adanosTrendingTool.request.url as (params: { + apiKey: string + assetType: 'crypto' | ' Crypto ' + source: 'polymarket' + limit: number + }) => string + + for (const assetType of ['crypto', ' Crypto '] as const) { + expect(buildUrl({ apiKey: 'key', assetType, source: 'polymarket', limit: 25 })).toBe( + 'https://api.adanos.org/reddit/crypto/v1/trending?limit=25' + ) + } + }) + + it('rejects unsupported stock sources and out-of-range limits', () => { + const buildStockUrl = adanosStockSentimentTool.request.url as (params: { + apiKey: string + ticker: string + source: string + }) => string + const buildTrendingUrl = adanosTrendingTool.request.url as (params: { + apiKey: string + assetType: 'stocks' + source: 'reddit' + limit: number + }) => string + + expect(() => buildStockUrl({ apiKey: 'key', ticker: 'AAPL', source: 'unsupported' })).toThrow( + 'Stock source must be reddit, x, news, or polymarket' + ) + expect(() => + buildTrendingUrl({ apiKey: 'key', assetType: 'stocks', source: 'reddit', limit: 101 }) + ).toThrow('Result limit must be an integer from 1 to 100') + }) +}) + +describe('Adanos response transforms', () => { + it('normalizes Polymarket stock activity and daily trends', async () => { + const result = await adanosStockSentimentTool.transformResponse!( + respond({ + ticker: 'TSLA', + company_name: 'Tesla, Inc.', + found: true, + trade_count: 48, + buzz_score: 71.2, + sentiment_score: 0.24, + trend: 'rising', + bullish_pct: 63, + bearish_pct: 21, + period_days: 7, + daily_trend: [ + { + date: '2026-07-18', + trade_count: 9, + buzz_score: 66.1, + sentiment_score: 0.2, + }, + ], + }), + { apiKey: 'key', ticker: 'TSLA', source: 'polymarket' } + ) + + expect(result.output).toMatchObject({ + assetType: 'stocks', + source: 'polymarket', + symbol: 'TSLA', + name: 'Tesla, Inc.', + activityCount: 48, + dailyTrend: [{ date: '2026-07-18', activityCount: 9 }], + }) + }) + + it('normalizes trending crypto assets to the shared output', async () => { + const result = await adanosTrendingTool.transformResponse!( + respond([ + { + symbol: 'BTC', + name: 'Bitcoin', + mentions: 420, + buzz_score: 82.4, + sentiment_score: 0.31, + trend: 'rising', + bullish_pct: 61, + bearish_pct: 17, + }, + ]), + { apiKey: 'key', assetType: 'crypto', source: 'news' } + ) + + expect(result.output.assets).toEqual([ + { + assetType: 'crypto', + source: 'reddit', + symbol: 'BTC', + name: 'Bitcoin', + activityCount: 420, + buzzScore: 82.4, + sentimentScore: 0.31, + trend: 'rising', + bullishPct: 61, + bearishPct: 17, + }, + ]) + }) + + it('normalizes market drivers without exposing source-specific raw fields', async () => { + const result = await adanosMarketSentimentTool.transformResponse!( + respond({ + trade_count: 210, + active_tickers: 14, + buzz_score: 65.5, + sentiment_score: 0.18, + trend: 'stable', + drivers: [{ ticker: 'NVDA', trade_count: 33, buzz_score: 79.4, sentiment_score: 0.27 }], + }), + { apiKey: 'key', assetType: 'stocks', source: 'polymarket' } + ) + + expect(result.output).toMatchObject({ + source: 'polymarket', + activityCount: 210, + activeAssets: 14, + drivers: [{ symbol: 'NVDA', activityCount: 33, buzzScore: 79.4, sentimentScore: 0.27 }], + }) + }) + + it('surfaces Adanos API error details', async () => { + await expect( + adanosCryptoSentimentTool.transformResponse!( + respond({ detail: 'Invalid or inactive API key' }, 401) + ) + ).rejects.toThrow('Invalid or inactive API key') + }) +}) + +describe('Adanos block configuration', () => { + it('routes each operation to a registered tool and coerces the limit', () => { + const selectTool = AdanosBlock.tools?.config?.tool as (params: { operation: string }) => string + const mapParams = AdanosBlock.tools?.config?.params as (params: { + operation: string + apiKey: string + limit: string + source: string + startDate: string + endDate: string + }) => Record + + expect(selectTool({ operation: 'market_sentiment' })).toBe('adanos_market_sentiment') + expect( + mapParams({ + operation: 'trending', + apiKey: 'key', + limit: '20', + source: '', + startDate: '', + endDate: '', + }) + ).toMatchObject({ limit: 20, source: undefined, startDate: undefined, endDate: undefined }) + }) + + it('hides stock source selection for crypto operations', () => { + const sourceBlock = AdanosBlock.subBlocks.find((subBlock) => subBlock.id === 'source') + + expect(sourceBlock?.condition).toEqual({ + field: 'operation', + value: ['stock_sentiment', 'trending', 'market_sentiment'], + and: { field: 'assetType', value: 'crypto', not: true }, + }) + }) +}) diff --git a/apps/sim/tools/adanos/crypto_sentiment.ts b/apps/sim/tools/adanos/crypto_sentiment.ts new file mode 100644 index 00000000000..519eff9a4a4 --- /dev/null +++ b/apps/sim/tools/adanos/crypto_sentiment.ts @@ -0,0 +1,81 @@ +import type { + AdanosCryptoSentimentParams, + AdanosCryptoSentimentResponse, + AdanosRawAsset, +} from '@/tools/adanos/types' +import { + buildAdanosUrl, + getAdanosBasePath, + getAdanosHeaders, + normalizeAsset, + readAdanosResponse, +} from '@/tools/adanos/utils' +import type { ToolConfig } from '@/tools/types' + +export const adanosCryptoSentimentTool: ToolConfig< + AdanosCryptoSentimentParams, + AdanosCryptoSentimentResponse +> = { + id: 'adanos_crypto_sentiment', + name: 'Adanos Crypto Sentiment', + description: 'Get Reddit market sentiment for a cryptocurrency', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Adanos API key', + }, + symbol: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Cryptocurrency symbol, such as BTC', + }, + startDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Start date in YYYY-MM-DD format', + }, + endDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'End date in YYYY-MM-DD format', + }, + }, + request: { + url: (params) => + buildAdanosUrl( + `${getAdanosBasePath('crypto')}/token/${encodeURIComponent(params.symbol.trim().toUpperCase())}`, + params + ), + method: 'GET', + headers: (params) => getAdanosHeaders(params.apiKey), + }, + transformResponse: async (response) => { + const data = await readAdanosResponse(response) + return { success: true, output: normalizeAsset(data, 'crypto', 'reddit') } + }, + outputs: { + assetType: { type: 'string', description: 'Asset type' }, + source: { type: 'string', description: 'Sentiment data source' }, + symbol: { type: 'string', description: 'Cryptocurrency symbol' }, + name: { type: 'string', description: 'Cryptocurrency name', optional: true }, + found: { type: 'boolean', description: 'Whether sentiment data was found' }, + buzzScore: { type: 'number', description: 'Buzz score from 0 to 100', optional: true }, + sentimentScore: { + type: 'number', + description: 'Sentiment score from -1 to 1', + optional: true, + }, + trend: { type: 'string', description: 'Current sentiment trend', optional: true }, + bullishPct: { type: 'number', description: 'Bullish mention percentage', optional: true }, + bearishPct: { type: 'number', description: 'Bearish mention percentage', optional: true }, + activityCount: { type: 'number', description: 'Reddit mention count' }, + periodDays: { type: 'number', description: 'Number of days in the period', optional: true }, + dailyTrend: { type: 'json', description: 'Normalized daily sentiment trend' }, + }, +} diff --git a/apps/sim/tools/adanos/index.ts b/apps/sim/tools/adanos/index.ts new file mode 100644 index 00000000000..8b8cceb4487 --- /dev/null +++ b/apps/sim/tools/adanos/index.ts @@ -0,0 +1,5 @@ +export { adanosCryptoSentimentTool } from './crypto_sentiment' +export { adanosMarketSentimentTool } from './market_sentiment' +export { adanosStockSentimentTool } from './stock_sentiment' +export { adanosTrendingTool } from './trending' +export * from './types' diff --git a/apps/sim/tools/adanos/market_sentiment.ts b/apps/sim/tools/adanos/market_sentiment.ts new file mode 100644 index 00000000000..7c06d59a684 --- /dev/null +++ b/apps/sim/tools/adanos/market_sentiment.ts @@ -0,0 +1,114 @@ +import type { + AdanosMarketSentimentParams, + AdanosMarketSentimentResponse, + AdanosRawMarketOverview, +} from '@/tools/adanos/types' +import { + buildAdanosUrl, + getActivityCount, + getAdanosBasePath, + getAdanosHeaders, + getAdanosSource, + normalizeDriver, + readAdanosResponse, +} from '@/tools/adanos/utils' +import type { ToolConfig } from '@/tools/types' + +export const adanosMarketSentimentTool: ToolConfig< + AdanosMarketSentimentParams, + AdanosMarketSentimentResponse +> = { + id: 'adanos_market_sentiment', + name: 'Adanos Market Sentiment', + description: 'Get aggregate market sentiment for stocks or cryptocurrencies', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Adanos API key', + }, + assetType: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Asset type: stocks or crypto', + }, + source: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Stock sentiment source: reddit, x, news, or polymarket', + }, + startDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Start date in YYYY-MM-DD format', + }, + endDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'End date in YYYY-MM-DD format', + }, + }, + request: { + url: (params) => + buildAdanosUrl( + `${getAdanosBasePath(params.assetType, params.source)}/market-sentiment`, + params + ), + method: 'GET', + headers: (params) => getAdanosHeaders(params.apiKey), + }, + transformResponse: async (response, params) => { + const data = await readAdanosResponse(response) + const assetType = params?.assetType ?? 'stocks' + const source = getAdanosSource(assetType, params?.source) + return { + success: true, + output: { + assetType, + source, + buzzScore: data.buzz_score ?? null, + sentimentScore: data.sentiment_score ?? null, + trend: data.trend ?? null, + bullishPct: data.bullish_pct ?? null, + bearishPct: data.bearish_pct ?? null, + activityCount: getActivityCount(data), + activeAssets: data.active_tickers ?? 0, + drivers: (data.drivers ?? []).map(normalizeDriver), + }, + } + }, + outputs: { + assetType: { type: 'string', description: 'Asset type' }, + source: { type: 'string', description: 'Sentiment data source' }, + buzzScore: { type: 'number', description: 'Market buzz score from 0 to 100', optional: true }, + sentimentScore: { + type: 'number', + description: 'Aggregate sentiment score from -1 to 1', + optional: true, + }, + trend: { type: 'string', description: 'Current market sentiment trend', optional: true }, + bullishPct: { type: 'number', description: 'Bullish activity percentage', optional: true }, + bearishPct: { type: 'number', description: 'Bearish activity percentage', optional: true }, + activityCount: { type: 'number', description: 'Aggregate mention or trade count' }, + activeAssets: { type: 'number', description: 'Number of active assets' }, + drivers: { + type: 'array', + description: 'Assets contributing most to market sentiment', + items: { + type: 'object', + properties: { + symbol: { type: 'string', description: 'Ticker or cryptocurrency symbol' }, + activityCount: { type: 'number', description: 'Mention or trade count' }, + buzzScore: { type: 'number', description: 'Buzz score from 0 to 100' }, + sentimentScore: { type: 'number', description: 'Sentiment score from -1 to 1' }, + }, + }, + }, + }, +} diff --git a/apps/sim/tools/adanos/stock_sentiment.ts b/apps/sim/tools/adanos/stock_sentiment.ts new file mode 100644 index 00000000000..abca1d55318 --- /dev/null +++ b/apps/sim/tools/adanos/stock_sentiment.ts @@ -0,0 +1,87 @@ +import type { + AdanosRawAsset, + AdanosStockSentimentParams, + AdanosStockSentimentResponse, +} from '@/tools/adanos/types' +import { + buildAdanosUrl, + getAdanosBasePath, + getAdanosHeaders, + normalizeAsset, + readAdanosResponse, +} from '@/tools/adanos/utils' +import type { ToolConfig } from '@/tools/types' + +export const adanosStockSentimentTool: ToolConfig< + AdanosStockSentimentParams, + AdanosStockSentimentResponse +> = { + id: 'adanos_stock_sentiment', + name: 'Adanos Stock Sentiment', + description: 'Get stock sentiment from Reddit, X / FinTwit, news, or Polymarket', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Adanos API key', + }, + ticker: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'US stock ticker symbol, such as AAPL', + }, + source: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Sentiment source: reddit, x, news, or polymarket', + }, + startDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Start date in YYYY-MM-DD format', + }, + endDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'End date in YYYY-MM-DD format', + }, + }, + request: { + url: (params) => + buildAdanosUrl( + `${getAdanosBasePath('stocks', params.source)}/stock/${encodeURIComponent(params.ticker.trim().toUpperCase())}`, + params + ), + method: 'GET', + headers: (params) => getAdanosHeaders(params.apiKey), + }, + transformResponse: async (response, params) => { + const data = await readAdanosResponse(response) + return { success: true, output: normalizeAsset(data, 'stocks', params?.source ?? 'reddit') } + }, + outputs: { + assetType: { type: 'string', description: 'Asset type' }, + source: { type: 'string', description: 'Sentiment data source' }, + symbol: { type: 'string', description: 'Stock ticker symbol' }, + name: { type: 'string', description: 'Company name', optional: true }, + found: { type: 'boolean', description: 'Whether sentiment data was found' }, + buzzScore: { type: 'number', description: 'Buzz score from 0 to 100', optional: true }, + sentimentScore: { + type: 'number', + description: 'Sentiment score from -1 to 1', + optional: true, + }, + trend: { type: 'string', description: 'Current sentiment trend', optional: true }, + bullishPct: { type: 'number', description: 'Bullish activity percentage', optional: true }, + bearishPct: { type: 'number', description: 'Bearish activity percentage', optional: true }, + activityCount: { type: 'number', description: 'Mention or trade count' }, + periodDays: { type: 'number', description: 'Number of days in the period', optional: true }, + dailyTrend: { type: 'json', description: 'Normalized daily sentiment trend' }, + }, +} diff --git a/apps/sim/tools/adanos/trending.ts b/apps/sim/tools/adanos/trending.ts new file mode 100644 index 00000000000..5f0e23ddd97 --- /dev/null +++ b/apps/sim/tools/adanos/trending.ts @@ -0,0 +1,112 @@ +import type { + AdanosRawAsset, + AdanosTrendingParams, + AdanosTrendingResponse, +} from '@/tools/adanos/types' +import { + buildAdanosUrl, + getActivityCount, + getAdanosBasePath, + getAdanosHeaders, + getAdanosSource, + readAdanosResponse, +} from '@/tools/adanos/utils' +import type { ToolConfig } from '@/tools/types' + +export const adanosTrendingTool: ToolConfig = { + id: 'adanos_trending', + name: 'Adanos Trending Assets', + description: 'List trending stocks or cryptocurrencies ranked by market sentiment activity', + version: '1.0.0', + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Adanos API key', + }, + assetType: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Asset type: stocks or crypto', + }, + source: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Stock sentiment source: reddit, x, news, or polymarket', + }, + limit: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of assets to return, from 1 to 100', + }, + startDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Start date in YYYY-MM-DD format', + }, + endDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'End date in YYYY-MM-DD format', + }, + }, + request: { + url: (params) => + buildAdanosUrl( + `${getAdanosBasePath(params.assetType, params.source)}/trending`, + params, + params.limit + ), + method: 'GET', + headers: (params) => getAdanosHeaders(params.apiKey), + }, + transformResponse: async (response, params) => { + const data = await readAdanosResponse(response) + const assetType = params?.assetType ?? 'stocks' + const source = getAdanosSource(assetType, params?.source) + return { + success: true, + output: { + assets: data.map((asset) => ({ + assetType, + source, + symbol: asset.ticker ?? asset.symbol ?? '', + name: asset.company_name ?? asset.name ?? null, + buzzScore: asset.buzz_score ?? null, + sentimentScore: asset.sentiment_score ?? null, + trend: asset.trend ?? null, + bullishPct: asset.bullish_pct ?? null, + bearishPct: asset.bearish_pct ?? null, + activityCount: getActivityCount(asset), + })), + }, + } + }, + outputs: { + assets: { + type: 'array', + description: 'Trending assets with normalized sentiment metrics', + items: { + type: 'object', + properties: { + assetType: { type: 'string', description: 'Asset type' }, + source: { type: 'string', description: 'Sentiment data source' }, + symbol: { type: 'string', description: 'Ticker or cryptocurrency symbol' }, + name: { type: 'string', description: 'Asset name' }, + buzzScore: { type: 'number', description: 'Buzz score from 0 to 100' }, + sentimentScore: { type: 'number', description: 'Sentiment score from -1 to 1' }, + trend: { type: 'string', description: 'Current sentiment trend' }, + bullishPct: { type: 'number', description: 'Bullish activity percentage' }, + bearishPct: { type: 'number', description: 'Bearish activity percentage' }, + activityCount: { type: 'number', description: 'Mention or trade count' }, + }, + }, + }, + }, +} diff --git a/apps/sim/tools/adanos/types.ts b/apps/sim/tools/adanos/types.ts new file mode 100644 index 00000000000..fbcab5f198c --- /dev/null +++ b/apps/sim/tools/adanos/types.ts @@ -0,0 +1,154 @@ +import type { ToolResponse } from '@/tools/types' + +export type AdanosStockSource = 'reddit' | 'x' | 'news' | 'polymarket' +export type AdanosAssetType = 'stocks' | 'crypto' + +export interface AdanosDateRangeParams { + apiKey: string + startDate?: string + endDate?: string +} + +export interface AdanosStockSentimentParams extends AdanosDateRangeParams { + ticker: string + source: AdanosStockSource +} + +export interface AdanosCryptoSentimentParams extends AdanosDateRangeParams { + symbol: string +} + +export interface AdanosTrendingParams extends AdanosDateRangeParams { + assetType: AdanosAssetType + source?: AdanosStockSource + limit?: number +} + +export interface AdanosMarketSentimentParams extends AdanosDateRangeParams { + assetType: AdanosAssetType + source?: AdanosStockSource +} + +export interface AdanosDailyTrend { + date: string + activityCount: number + sentimentScore: number | null + buzzScore: number | null + bullishPct: number | null + bearishPct: number | null +} + +export interface AdanosAssetSentiment { + assetType: AdanosAssetType + source: AdanosStockSource + symbol: string + name: string | null + found: boolean + buzzScore: number | null + sentimentScore: number | null + trend: string | null + bullishPct: number | null + bearishPct: number | null + activityCount: number + periodDays: number | null + dailyTrend: AdanosDailyTrend[] +} + +export interface AdanosTrendingAsset { + assetType: AdanosAssetType + source: AdanosStockSource + symbol: string + name: string | null + buzzScore: number | null + sentimentScore: number | null + trend: string | null + bullishPct: number | null + bearishPct: number | null + activityCount: number +} + +export interface AdanosMarketDriver { + symbol: string + activityCount: number + buzzScore: number | null + sentimentScore: number | null +} + +export interface AdanosMarketOverview { + assetType: AdanosAssetType + source: AdanosStockSource + buzzScore: number | null + sentimentScore: number | null + trend: string | null + bullishPct: number | null + bearishPct: number | null + activityCount: number + activeAssets: number + drivers: AdanosMarketDriver[] +} + +export interface AdanosStockSentimentResponse extends ToolResponse { + output: AdanosAssetSentiment +} + +export interface AdanosCryptoSentimentResponse extends ToolResponse { + output: AdanosAssetSentiment +} + +export interface AdanosTrendingResponse extends ToolResponse { + output: { + assets: AdanosTrendingAsset[] + } +} + +export interface AdanosMarketSentimentResponse extends ToolResponse { + output: AdanosMarketOverview +} + +export interface AdanosRawDailyTrend { + date?: string + mentions?: number + trade_count?: number + sentiment_score?: number | null + buzz_score?: number | null + bullish_pct?: number | null + bearish_pct?: number | null +} + +export interface AdanosRawAsset { + ticker?: string + symbol?: string + company_name?: string | null + name?: string | null + found?: boolean + buzz_score?: number | null + sentiment_score?: number | null + trend?: string | null + bullish_pct?: number | null + bearish_pct?: number | null + mentions?: number + trade_count?: number + period_days?: number | null + daily_trend?: AdanosRawDailyTrend[] +} + +export interface AdanosRawMarketDriver { + ticker?: string + symbol?: string + mentions?: number + trade_count?: number + buzz_score?: number | null + sentiment_score?: number | null +} + +export interface AdanosRawMarketOverview { + buzz_score?: number | null + sentiment_score?: number | null + trend?: string | null + bullish_pct?: number | null + bearish_pct?: number | null + mentions?: number + trade_count?: number + active_tickers?: number + drivers?: AdanosRawMarketDriver[] +} diff --git a/apps/sim/tools/adanos/utils.ts b/apps/sim/tools/adanos/utils.ts new file mode 100644 index 00000000000..75c3ae57c6e --- /dev/null +++ b/apps/sim/tools/adanos/utils.ts @@ -0,0 +1,124 @@ +import type { + AdanosAssetType, + AdanosDateRangeParams, + AdanosRawAsset, + AdanosRawDailyTrend, + AdanosRawMarketDriver, + AdanosStockSource, +} from '@/tools/adanos/types' + +const ADANOS_BASE_URL = 'https://api.adanos.org' + +const STOCK_SOURCE_PATHS: Record = { + reddit: '/reddit/stocks/v1', + x: '/x/stocks/v1', + news: '/news/stocks/v1', + polymarket: '/polymarket/stocks/v1', +} + +function isCryptoAssetType(assetType: AdanosAssetType) { + return assetType.trim().toLowerCase() === 'crypto' +} + +export function getAdanosSource(assetType: AdanosAssetType, source?: AdanosStockSource) { + return isCryptoAssetType(assetType) ? 'reddit' : (source ?? 'reddit') +} + +export function getAdanosBasePath(assetType: AdanosAssetType, source?: AdanosStockSource) { + if (isCryptoAssetType(assetType)) { + return '/reddit/crypto/v1' + } + + const sourcePath = STOCK_SOURCE_PATHS[source ?? 'reddit'] + if (!sourcePath) { + throw new Error('Stock source must be reddit, x, news, or polymarket') + } + + return sourcePath +} + +export function buildAdanosUrl(path: string, params: AdanosDateRangeParams, limit?: number) { + const url = new URL(path, ADANOS_BASE_URL) + + if (params.startDate?.trim()) { + url.searchParams.set('from', params.startDate.trim()) + } + if (params.endDate?.trim()) { + url.searchParams.set('to', params.endDate.trim()) + } + if (limit !== undefined) { + if (!Number.isInteger(limit) || limit < 1 || limit > 100) { + throw new Error('Result limit must be an integer from 1 to 100') + } + url.searchParams.set('limit', String(limit)) + } + + return url.toString() +} + +export function getAdanosHeaders(apiKey: string) { + return { + Accept: 'application/json', + 'X-API-Key': apiKey.trim(), + } +} + +export function getActivityCount(data: { mentions?: number; trade_count?: number }) { + return data.mentions ?? data.trade_count ?? 0 +} + +export function normalizeDailyTrend(items: AdanosRawDailyTrend[] | undefined) { + return (items ?? []).map((item) => ({ + date: item.date ?? '', + activityCount: getActivityCount(item), + sentimentScore: item.sentiment_score ?? null, + buzzScore: item.buzz_score ?? null, + bullishPct: item.bullish_pct ?? null, + bearishPct: item.bearish_pct ?? null, + })) +} + +export function normalizeAsset( + data: AdanosRawAsset, + assetType: AdanosAssetType, + source: AdanosStockSource +) { + return { + assetType, + source, + symbol: data.ticker ?? data.symbol ?? '', + name: data.company_name ?? data.name ?? null, + found: data.found ?? false, + buzzScore: data.buzz_score ?? null, + sentimentScore: data.sentiment_score ?? null, + trend: data.trend ?? null, + bullishPct: data.bullish_pct ?? null, + bearishPct: data.bearish_pct ?? null, + activityCount: getActivityCount(data), + periodDays: data.period_days ?? null, + dailyTrend: normalizeDailyTrend(data.daily_trend), + } +} + +export function normalizeDriver(data: AdanosRawMarketDriver) { + return { + symbol: data.ticker ?? data.symbol ?? '', + activityCount: getActivityCount(data), + buzzScore: data.buzz_score ?? null, + sentimentScore: data.sentiment_score ?? null, + } +} + +export async function readAdanosResponse(response: Response): Promise { + const data = (await response.json()) as T & { + detail?: string | { message?: string } + message?: string + } + + if (!response.ok) { + const detail = typeof data.detail === 'string' ? data.detail : data.detail?.message + throw new Error(detail ?? data.message ?? `Adanos API request failed (${response.status})`) + } + + return data +} diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index 9c301e67b98..b8cfae774a6 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -4,6 +4,12 @@ import { a2aGetTaskTool, a2aSendMessageTool, } from '@/tools/a2a' +import { + adanosCryptoSentimentTool, + adanosMarketSentimentTool, + adanosStockSentimentTool, + adanosTrendingTool, +} from '@/tools/adanos' import { agentmailCreateDraftTool, agentmailCreateInboxTool, @@ -4709,6 +4715,10 @@ export const tools: Record = { a2a_get_agent_card: a2aGetAgentCardTool, a2a_get_task: a2aGetTaskTool, a2a_send_message: a2aSendMessageTool, + adanos_crypto_sentiment: adanosCryptoSentimentTool, + adanos_market_sentiment: adanosMarketSentimentTool, + adanos_stock_sentiment: adanosStockSentimentTool, + adanos_trending: adanosTrendingTool, agentmail_create_draft: agentmailCreateDraftTool, agentmail_create_inbox: agentmailCreateInboxTool, agentmail_delete_draft: agentmailDeleteDraftTool,