Remove finance-related code and dependencies.

This commit entirely removes the financial query agent, market data tools, and associated code and dependencies. It simplifies the codebase by eliminating unused or unnecessary functionality related to cryptocurrency market data and financial analysis.
This commit is contained in:
geoffsee
2025-05-27 12:30:19 -04:00
parent cfe77d7e61
commit 6e6865e0aa
18 changed files with 0 additions and 861 deletions

View File

@@ -1,4 +0,0 @@
export * from "../genaiscript/genaisrc/_state";
export * from "./news";
export * from "./quotes";
export * from "./types";

View File

@@ -1,14 +0,0 @@
import {ApiResponse} from "./types";
export async function collect_gainers_losers(x: { apiKey: string, limit: number }): Promise<ApiResponse> {
const { apiKey, limit } = x;
//
const data: ApiResponse = await fetch(`https://pro-api.coinmarketcap.com/v1/cryptocurrency/trending/gainers-losers?limit=${limit}`, {
headers: {
"x-cmc_pro_api_key": apiKey
}
}).then((symbolDataRequest) => symbolDataRequest.json());
return data;
}

View File

@@ -1,48 +0,0 @@
type Quote = {
price: number;
volume_24h: number;
percent_change_1h: number;
percent_change_24h: number;
percent_change_7d: number;
market_cap: number;
last_updated: string;
};
type Platform = null;
type Tag = string;
type Data = {
id: number;
name: string;
symbol: string;
slug: string;
cmc_rank?: number;
num_market_pairs: number;
circulating_supply: number;
total_supply: number;
max_supply: number;
last_updated: string;
date_added: string;
tags: Tag[];
platform: Platform;
quote: {
USD: Quote;
BTC?: Quote;
ETH?: Quote;
};
};
type Status = {
timestamp: string;
error_code: number;
error_message: string | null;
elapsed: number;
credit_count: number;
};
export type ApiResponse = {
data: Data[];
status: Status;
};

View File

@@ -1,178 +0,0 @@
import {types, Instance} from 'mobx-state-tree';
import {runInAction} from "mobx";
const Article = types.model('Article', {
title: types.string,
content: types.string,
url: types.maybe(types.string),
source: types.maybe(types.string),
pubDate: types.maybe(types.string),
summary: types.maybe(types.string),
description: types.maybe(types.string),
authorsByline: types.maybe(types.string),
shortSummary: types.maybe(types.string),
labels: types.maybe(types.frozen()),
imageUrl: types.maybe(types.string),
score: types.maybe(types.number),
});
export const NewsStore = types
.model('NewsStore', {
symbolsNews: types.map(types.array(Article)),
isLoading: types.boolean,
error: types.maybe(types.string),
apiKey: types.string,
})
.actions((self) => ({
addNews(symbol: string, articles: any[]) {
if (!self.symbolsNews.has(symbol)) {
self.symbolsNews.set(symbol, []);
}
const mappedArticles = articles.map((article) => Article.create({
title: article.title || 'No Title',
content: article.content || 'No Content',
url: article.url,
source: article.domain,
pubDate: article.pubDate,
summary: article.summary,
description: article.description,
authorsByline: article.authorsByline,
shortSummary: article.shortSummary,
labels: article.labels,
imageUrl: article.imageUrl,
score: article.score,
}));
self.symbolsNews.get(symbol)!.push(...mappedArticles);
self.isLoading = false;
},
clearNews(symbol: string) {
if (self.symbolsNews.has(symbol)) {
self.symbolsNews.set(symbol, []);
}
},
setLoading(loading: boolean) {
self.isLoading = loading;
},
setError(message: string) {
self.error = message;
self.isLoading = false;
},
async fetchNewsForSymbol(symbol: string, limit: number, sort: "date" | "relevance") {
self.setLoading(true);
self.setError(undefined);
try {
await runInAction(async () => {
const newsData = await collect_news({symbol, apiKey: self.apiKey, limit, sort});
if (newsData && newsData.articles) {
self.addNews(symbol, newsData.articles);
} else {
self.setError("Failed to fetch news or invalid response format.");
}
})
} catch (err: any) {
console.error('Error fetching news:', err);
self.setError(err.message || "Failed to fetch news.");
}
},
}))
.views((self) => ({
getNewsForSymbol(symbol: string) {
return self.symbolsNews.get(symbol) || [];
},
getAllSymbols() {
return Array.from(self.symbolsNews.keys());
},
hasNewsForSymbol(symbol: string) {
return self.symbolsNews.has(symbol) && self.symbolsNews.get(symbol)!.length > 0;
},
}));
export type INewsStore = Instance<typeof NewsStore>;
export const createNewsStore = (apikey, perigon) => NewsStore.create({
symbolsNews: {},
isLoading: false,
error: undefined,
apiKey: apikey,
});
/* @collect_news return value structure
{
news: {
status: 200,
numResults: 4080,
articles: [
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object], [Object]
]
}
}
*/
export async function collect_news(x: { symbol: string, apiKey: string, limit: number, sort: "date" | "relevance" }) {
const {symbol, apiKey, limit, sort} = x;
const symbolNameMap = {
"BTC": "Bitcoin",
"ETH": "Ethereum",
"XRP": "Ripple",
"LTC": "Litecoin",
"ADA": "Cardano",
"DOGE": "Dogecoin",
"BNB": "Binance Coin",
"DOT": "Polkadot",
"SOL": "Solana",
"AVAX": "Avalanche"
};
const cryptocurrencyName = symbolNameMap[symbol] ?? symbol;
const rawContentQuery = "scandal OR \"corporate misconduct*\" OR fraud OR \"financial irregularities*\" OR lawsuit OR \"legal action*\" OR bankruptcy OR \"financial distress*\" OR \"data breach\" OR \"security vulnerability*\" OR \"environmental impact\" OR \"ecological damage*\" OR \"labor dispute\" OR \"worker rights*\" OR \"product failure\" OR \"quality issue*\" OR \"ethical concern\" OR \"moral dilemma*\" OR \"health risk\" OR \"safety hazard*\" OR \"regulatory violation\" OR \"compliance issue*\" OR \"market manipulation\" OR \"trading irregularity*\" OR \"public relations crisis\" OR \"reputation damage*\" OR \"political controversy\" OR \"government intervention*\" OR \"consumer complaint\" OR \"customer dissatisfaction*\" OR \"supply chain disruption\" OR \"logistics problem*\" OR \"intellectual property dispute\" OR \"patent infringement*\"";
const contentQuery = encodeURIComponent(rawContentQuery);
const rawTitleQuery = `${cryptocurrencyName} OR ${symbol} OR "${cryptocurrencyName} price" OR "${cryptocurrencyName} market" OR "${cryptocurrencyName} news"`;
const titleQuery = encodeURIComponent(rawTitleQuery);
try {
const result = await allNews({
q: contentQuery,
title: titleQuery,
size: limit,
sortBy: sort,
apiKey: apiKey
});
return result.data;
} catch (err) {
console.error('Error fetching news:', err);
throw err;
}
}

View File

@@ -1,75 +0,0 @@
import {describe, expect, it} from 'vitest';
import {collect_news, createNewsStore, NewsStore} from './index';
const testApiKey = '';
describe('NewsStore', () => {
it('should create a NewsStore instance', () => {
const store = createNewsStore(testApiKey);
expect(store).toBeDefined();
expect(store.isLoading).toBe(false);
expect(store.error).toBeUndefined();
expect(store.getAllSymbols()).toEqual([]);
});
it('should add news articles for a symbol', () => {
const store = createNewsStore(testApiKey);
const articles = [
{ title: 'Article 1', content: 'Content 1', url: 'http://example.com/1', source: 'Source 1', publishedAt: '2025-01-01' },
{ title: 'Article 2', content: 'Content 2', url: 'http://example.com/2', source: 'Source 2', publishedAt: '2025-01-02' }
];
store.addNews('BTC', articles);
expect(store.getNewsForSymbol('BTC')).toHaveLength(2);
expect(store.getNewsForSymbol('BTC')[0].title).toBe('Article 1');
expect(store.getNewsForSymbol('BTC')[1].title).toBe('Article 2');
expect(store.hasNewsForSymbol('BTC')).toBe(true);
});
it('should clear news articles for a symbol', () => {
const store = createNewsStore(testApiKey);
const articles = [
{ title: 'Article 1', content: 'Content 1', url: 'http://example.com/1', source: 'Source 1', publishedAt: '2025-01-01' }
];
store.addNews('BTC', articles);
store.clearNews('BTC');
expect(store.getNewsForSymbol('BTC')).toHaveLength(0);
expect(store.hasNewsForSymbol('BTC')).toBe(false);
});
it('should handle fetchNewsForSymbol successfully', async () => {
const store = createNewsStore(testApiKey);
await store.fetchNewsForSymbol('BTC', 10, 'date');
const storeNews = store.getNewsForSymbol('BTC');
console.log(storeNews);
expect(storeNews).toHaveLength(10);
expect(store.getNewsForSymbol('BTC')[0].title).toBeTypeOf("string");
expect(store.isLoading).toBe(false);
expect(store.error).toBeUndefined();
});
it('should throw an error for invalid symbol in collect_news', async () => {
await expect(collect_news({ symbol: 'INVALID', apiKey: testApiKey, limit: 10, sort: 'date' }))
.rejects.toThrow('Invalid symbol: INVALID. Must be one of BTC, ETH, XRP, LTC, ADA, DOGE, BNB, DOT, SOL, AVAX.');
});
it('should fetch news using collect_news', async () => {
const result = await collect_news({ symbol: 'BTC', apiKey: testApiKey, limit: 1, sort: 'date' });
expect(result).toBeDefined();
expect(result.status).toBe(200);
expect(result.articles).toBeDefined();
});
});

View File

@@ -1,9 +0,0 @@
{
"name": "@web-agent-rs/core",
"version": "1.0.0",
"type": "module",
"dependencies": {
"mobx-state-tree": "^7.0.2",
"@web-agent-rs/perigon": "workspace:*"
}
}

View File

@@ -1,137 +0,0 @@
import {getSnapshot, types} from "mobx-state-tree";
import {NewsStore} from "../news";
const PortfolioCashModel = types.model("PortfolioCash", {
amount: types.number,
currency: types.enumeration("Currency", ["USD", "BTC"]),
});
const PortfolioActionModel = types.model("PortfolioAction", {
action: types.enumeration("Action", ["buy", "sell", "hold"]),
symbol: types.string,
quantity: types.number,
timestamp: types.Date,
});
export const PortfolioNewsportfolioNewsModel = types.model("PortfolioNews", {
symbol: types.string,
date_created: types.string,
news: types.array(types.model("NewsItem", {
symbol: types.maybe(types.string),
date_created: types.maybe(types.string),
news: types.string,
timestamp: types.maybe(types.string),
})),
timestamp: types.maybe(types.string),
});
export const portfolioQuoteModel = types.model("PortfolioQuote", {
symbol: types.string,
quote: types.string,
date_created: types.string,
});
const PortfolioAssetContextModel = types.model("PortfolioAssetContext", {
timestamp: types.Date,
portfolio_snapshot: PortfolioCashModel,
});
const PortfolioAssetModel = types.model("PortfolioAsset", {
symbol: types.string,
quantity: types.number,
recommended_action: types.maybe(
types.enumeration("RecommendedAction", ["buy", "sell", "hold"])
),
last_taken_action: types.optional(types.enumeration("LastAction", ["buy", "sell", "hold", "none", "never"]), "never"),
context: PortfolioAssetContextModel,
});
const PortfolioModel = types
.model("Portfolio", {
supportedSymbols: types.array(types.string),
liquidity: PortfolioCashModel,
actions: types.optional(types.array(PortfolioActionModel), []),
assets: types.array(PortfolioAssetModel),
news: types.optional(types.array(NewsStore), []),
quotes: types.optional(types.array(portfolioQuoteModel), []),
})
.actions((self) => ({
addAction(actionData: {
action: "buy" | "sell" | "hold";
symbol: string;
quantity: number;
}) {
self.actions.push({
...actionData,
timestamp: new Date(),
});
},
addNews(newsData: any) {
self.news.push({
...newsData,
timestamp: new Date(),
});
},
addQuote(quoteData: any) {
self.quotes
self.quotes.push({
...quoteData,
timestamp: new Date(),
});
},
updateLiquidity(amount: number) {
self.liquidity.amount = amount;
},
}));
const tokenList = [
"AAVE", "AVAX", "BAT", "BCH", "BTC",
"CRV", "DOGE", "DOT", "ETH", "GRT",
"LINK", "LTC", "MKR", "SHIB", "SUSHI",
"UNI", "USDC", "USDT", "XTZ", "YFI",
];
const portfolioCash = PortfolioCashModel.create({
amount: 10000,
currency: "USD",
});
const portfolioAssets = tokenList.map((token) =>
PortfolioAssetModel.create({
symbol: token,
quantity: 0,
recommended_action: "hold",
last_taken_action: undefined,
context: {
timestamp: new Date(),
portfolio_snapshot: getSnapshot(portfolioCash),
},
})
);
const portfolioActions = [];
const portfolioNews = [];
const portfolioQuotes = [];
const portfolioInstance = PortfolioModel.create({
liquidity: portfolioCash,
actions: portfolioActions,
assets: portfolioAssets,
supportedSymbols: tokenList,
news: portfolioNews,
quotes: portfolioQuotes
});
export default portfolioInstance;

View File

@@ -1,13 +0,0 @@
import {ApiResponse} from "./types";
export async function collect_quote(x: { symbol: string, apiKey: string }) {
const {symbol, apiKey} = x;
const data: ApiResponse = await fetch(`https://pro-api.coinmarketcap.com/v2/cryptocurrency/quotes/latest?symbol=${symbol}`, {
headers: {
"x-cmc_pro_api_key": apiKey
}
}).then((symbolDataRequest) => symbolDataRequest.json());
return data;
}

View File

@@ -1,77 +0,0 @@
import {types, flow, Instance} from "mobx-state-tree";
import {collect_quote} from './index';
const QuoteData = types.optional(types.frozen(), {})
export const QuoteStore = types
.model("QuoteStore", {
apiKey: types.string,
quotes: types.map(QuoteData),
})
.views(self => ({
getQuote(symbol) {
return self.quotes.get(symbol);
},
hasQuote(symbol) {
return self.quotes.has(symbol);
}
}))
.actions(self => {
const extractUsefulData = (data, symbol) => {
return data.data[symbol].map(qd => ({
symbol: qd.symbol,
slug: qd.slug,
tags: qd.tags,
id: qd.id,
...qd.quote.USD
})).at(0);
};
const fetchQuote = flow(function* (symbol) {
try {
const data = yield collect_quote({symbol, apiKey: self.apiKey});
const usefulData = extractUsefulData(data, symbol);
self.quotes.set(symbol, usefulData);
return usefulData;
} catch (error) {
console.error(`An error occurred fetching the quote for symbol: ${symbol}`, error);
throw error;
}
});
const fetchQuotes = flow(function* (symbols) {
const results = {};
for (const symbol of symbols) {
if (self.quotes.has(symbol)) {
results[symbol] = self.quotes.get(symbol);
} else {
const data = yield fetchQuote(symbol);
results[symbol] = extractUsefulData(data, symbol);
}
}
return results;
});
const clearCache = () => {
self.quotes.clear();
};
return {
fetchQuote,
fetchQuotes,
clearCache
};
});
export type QuoteManagerType = Instance<typeof QuoteStore>;

View File

@@ -1,17 +0,0 @@
import {describe, it} from 'vitest';
import {QuoteStore} from "./models";
describe('QuoteStore', () => {
it('should get data for symbols using the quoteManager', async () => {
const testApiKey = '';
const quoteManager = QuoteStore.create({
apiKey: testApiKey,
});
const symbol = 'BTC';
const data = await quoteManager.fetchQuote(symbol);
console.log(JSON.stringify(data));
});
});

View File

@@ -1,71 +0,0 @@
type Status = {
timestamp: string;
error_code: number;
error_message: string | null;
elapsed: number;
credit_count: number;
notice: string | null;
};
type Tag = {
slug: string;
name: string;
category: string;
};
type Quote = {
USD: {
price: number | null;
volume_24h: number;
volume_change_24h: number;
percent_change_1h: number;
percent_change_24h: number;
percent_change_7d: number;
percent_change_30d: number;
percent_change_60d: number;
percent_change_90d: number;
market_cap: number | null;
market_cap_dominance: number | null;
fully_diluted_market_cap: number | null;
tvl: number | null;
last_updated: string;
};
};
type Platform = {
id: number;
name: string;
symbol: string;
slug: string;
token_address: string;
};
type Cryptocurrency = {
id: number;
name: string;
symbol: string;
slug: string;
num_market_pairs: number;
date_added: string;
tags: Tag[];
max_supply: number | null;
circulating_supply: number | null;
total_supply: number;
platform: Platform | null;
is_active: number;
infinite_supply: boolean;
cmc_rank: number | null;
is_fiat: number;
self_reported_circulating_supply: number | null;
self_reported_market_cap: number | null;
tvl_ratio: number | null;
last_updated: string;
quote: Quote;
};
export type ApiResponse = {
status: Status;
data: {
[SYMBOL: string]: Cryptocurrency[];
};
};

View File

@@ -1,69 +0,0 @@
export type CryptoDataResponse = {
status: {
timestamp: string;
error_code: number;
error_message: string | null;
elapsed: number;
credit_count: number;
notice: string | null;
};
data: {
[key: string]: CryptoAsset[];
};
};
export type CryptoAsset = {
id: number;
name: string;
symbol: string;
slug: string;
num_market_pairs: number;
date_added: string;
tags: CryptoTag[];
max_supply: number | null;
circulating_supply: number;
total_supply: number;
platform: CryptoPlatform | null;
is_active: number;
infinite_supply: boolean;
cmc_rank: number;
is_fiat: number;
self_reported_circulating_supply: number | null;
self_reported_market_cap: number | null;
tvl_ratio: number;
last_updated: string;
quote: {
[currency: string]: CryptoQuote;
};
};
export type CryptoTag = {
slug: string;
name: string;
category: string;
};
export type CryptoPlatform = {
id: number;
name: string;
symbol: string;
slug: string;
token_address: string;
};
export type CryptoQuote = {
price: number;
volume_24h: number;
volume_change_24h: number;
percent_change_1h: number;
percent_change_24h: number;
percent_change_7d: number;
percent_change_30d: number;
percent_change_60d: number;
percent_change_90d: number;
market_cap: number;
market_cap_dominance: number;
fully_diluted_market_cap: number;
tvl: number;
last_updated: string;
};

View File

@@ -1,21 +0,0 @@
import {types} from "mobx-state-tree";
import {QuoteStore} from "@web-agent-rs/core/quotes/models";
import {NewsStore} from "@web-agent-rs/core/news";
import newsStore from "./news";
import quoteStore from "./quotes";
const StateModel = types.model("State", {
symbols: types.array(types.string),
quotes: QuoteStore,
news: NewsStore,
});
const state = StateModel.create({
quotes: quoteStore,
news: newsStore,
});
export default state;

View File

@@ -1,11 +0,0 @@
import {NewsStore} from "@web-agent-rs/core/news";
import {Instance} from "mobx-state-tree";
const newsStore = NewsStore.create({
isLoading: false,
apiKey: process.env.PERIGON_API_KEY
});
export type NewsStore = Instance<typeof newsStore>;
export default newsStore;

View File

@@ -1,7 +0,0 @@
import {QuoteStore} from "@web-agent-rs/core/quotes/models";
const quoteStore = QuoteStore.create({
apiKey: process.env.CCC_API_KEY
});
export default quoteStore;

View File

@@ -1,80 +0,0 @@
import state from "./_state/index.js";
import {getSnapshot} from "mobx-state-tree";
import {collect_gainers_losers} from "@web-agent-rs/core/market";
def("QUERY", env.vars.user_input);
defTool(
"get_quote",
"Fetch quote for symbol",
{
"symbol": {
type: "string",
default: "BTC"
}
},
async (args) => {
const { symbol } = args;
await state.quotes.fetchQuote(symbol);
const quote = await state.quotes.getQuote(symbol);
return JSON.stringify(quote)
}
);
defTool(
"get_news",
"Fetches news for symbol",
{
"symbol": {
type: "string",
default: "BTC"
}
},
async (args) => {
const { symbol } = args;
await state.news.fetchNewsForSymbol(symbol, 5, "date");
const news = await state.news.getNewsForSymbol(symbol).map(i => getSnapshot(i));
return news
}
);
defTool(
"get_market",
"Fetches trending symbols of market",
{
"limit": {
type: "number",
default: "25"
}
},
async (args) => {
const { limit } = args;
const marketOverviewRequest = await collect_gainers_losers({apiKey: process.env.CCC_API_KEY, limit: parseInt(limit) })
return marketOverviewRequest.data.map(item => ({
symbol: item.symbol,
name: item.name,
change_1h: item.quote.USD.percent_change_1h,
price: item.quote.USD.price,
volume_24h: item.quote.USD.volume_24h
}))
}
);
$`You are a market data assistant specializing in financial analysis. Respond to QUERIES with accurate, clear, and concise information relevant to professionals in the finance sector. Use available tools efficiently to gather and present quantitative data.`;

View File

@@ -1,28 +0,0 @@
use tokio::process::Child;
use tracing;
use crate::utils::utils::run_agent;
pub async fn finance_query_agent(stream_id: &str, input: &str) -> Result<Child, String> {
run_agent(stream_id, input, "./packages/genaiscript/genaisrc/finance-query.genai.mts").await
}
// #[cfg(test)]
// mod tests {
// use std::fmt::Debug;
// use crate::agents::search::search_agent;
//
// #[tokio::test] // Mark the test function as async
// async fn test_search_execution() {
// let input = "Who won the 2024 presidential election?";
//
// let mut command = search_agent("test-stream", input).await.unwrap();
//
// // command.stdout.take().unwrap().read_to_string(&mut String::new()).await.unwrap();
// // Optionally, you can capture and inspect stdout if needed:
// let output = command.wait_with_output().await.expect("Failed to wait for output");
// println!("Stdout: {}", String::from_utf8_lossy(&output.stdout));
// println!("Stderr: {}", String::from_utf8_lossy(&output.stderr));
// }
// }

View File

@@ -18,7 +18,6 @@ use std::time::Duration;
use tokio::io::{AsyncBufReadExt, BufReader}; use tokio::io::{AsyncBufReadExt, BufReader};
use tokio::process::Command; use tokio::process::Command;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::agents::crypto_market::finance_query_agent;
use crate::agents::image_generator::image_generator; use crate::agents::image_generator::image_generator;
// init sled // init sled
@@ -100,7 +99,6 @@ pub async fn handle_webhooks(Path(stream_id): Path<String>) -> impl IntoResponse
"web-search" => search_agent(stream_id.as_str(), &*input).await, "web-search" => search_agent(stream_id.as_str(), &*input).await,
"news-search" => news_agent(stream_id.as_str(), &*input).await, "news-search" => news_agent(stream_id.as_str(), &*input).await,
"image-generator" => image_generator(stream_id.as_str(), &*input).await, "image-generator" => image_generator(stream_id.as_str(), &*input).await,
"finance-query" => finance_query_agent(stream_id.as_str(), &*input).await,
"web-scrape" => scrape_agent(stream_id.as_str(), &*input).await, "web-scrape" => scrape_agent(stream_id.as_str(), &*input).await,
_ => { _ => {
tracing::error!("Unsupported resource type: {}", resource); tracing::error!("Unsupported resource type: {}", resource);