import { defineSource } from '@jaspers-ai/sdk' import { z } from 'zod' import { HOSTS, baseField, list, upper } from './client' // Ticker news, newest first, with the per-ticker sentiment insights Massive attaches. export const news = defineSource({ description: 'News articles, newest first: published (ISO), title, publisher, author, url, summary, tickers, keywords, and sentiment insights (ticker: sentiment, reasoning). Pass one or more tickers, or none for all news.', hosts: HOSTS, input: z.object({ tickers: z.array(z.string()).max(20).default([]).describe('Tickers whose news to read, like AAPL. Empty for all news.'), from: z.string().optional().describe('Earliest publish time, YYYY-MM-DD or ISO date-time (published_utc.gte).'), to: z.string().optional().describe('Latest publish time, YYYY-MM-DD or ISO date-time (published_utc.lte).'), limit: z.number().int().min(1).max(1000).default(50).describe('Most articles to answer, across all tickers.'), base: baseField, }), async run(args, ctx) { const tickers = args.tickers.map(upper).filter(Boolean) const per = Math.min(args.limit, 1000) const read = (ticker?: string) => list( ctx, args.base, '/v2/reference/news', { ticker, 'published_utc.gte': args.from, 'published_utc.lte': args.to, order: 'desc', sort: 'published_utc', limit: per }, args.limit, ) const results = await Promise.all(tickers.length ? tickers.map((t) => read(t)) : [read()]) const seen = new Set() const articles: any[] = [] for (const r of results) for (const a of r.rows) { const key = String(a.id ?? a.article_url) if (!seen.has(key)) { seen.add(key) articles.push(a) } } articles.sort((a, b) => String(b.published_utc).localeCompare(String(a.published_utc))) const rows = articles.slice(0, args.limit).map((a) => { const insights = (Array.isArray(a.insights) ? a.insights : []) as any[] const mine = insights.find((i) => tickers.includes(String(i.ticker))) ?? insights[0] return { id: a.id ?? null, published: a.published_utc ?? null, title: a.title ?? null, publisher: a.publisher?.name ?? null, author: a.author ?? null, url: a.article_url ?? null, summary: a.description ?? null, image_url: a.image_url ?? null, tickers: Array.isArray(a.tickers) ? a.tickers.join(', ') : null, keywords: Array.isArray(a.keywords) ? a.keywords.join(', ') : null, sentiment: mine?.sentiment ?? null, sentiment_ticker: mine?.ticker ?? null, sentiment_reasoning: mine?.sentiment_reasoning ?? null, insights: insights.map((i) => `${i.ticker}: ${i.sentiment}`).join(', ') || null, } }) return { rows, more: results.some((r) => r.more) } }, })