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:
@@ -1,4 +0,0 @@
|
|||||||
export * from "../genaiscript/genaisrc/_state";
|
|
||||||
export * from "./news";
|
|
||||||
export * from "./quotes";
|
|
||||||
export * from "./types";
|
|
@@ -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;
|
|
||||||
}
|
|
@@ -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;
|
|
||||||
};
|
|
||||||
|
|
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -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();
|
|
||||||
});
|
|
||||||
});
|
|
@@ -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:*"
|
|
||||||
}
|
|
||||||
}
|
|
@@ -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;
|
|
@@ -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;
|
|
||||||
}
|
|
@@ -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>;
|
|
@@ -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));
|
|
||||||
});
|
|
||||||
});
|
|
@@ -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[];
|
|
||||||
};
|
|
||||||
};
|
|
@@ -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;
|
|
||||||
};
|
|
@@ -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;
|
|
@@ -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;
|
|
@@ -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;
|
|
@@ -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.`;
|
|
||||||
|
|
||||||
|
|
@@ -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));
|
|
||||||
// }
|
|
||||||
// }
|
|
@@ -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);
|
||||||
|
Reference in New Issue
Block a user