This commit is contained in:
geoffsee
2025-05-23 09:48:26 -04:00
commit 66d3c06230
84 changed files with 6529 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
# auto-generated
genaiscript.d.ts
tsconfig.json
jsconfig.json

View File

@@ -0,0 +1,21 @@
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

@@ -0,0 +1,11 @@
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

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

View File

@@ -0,0 +1,24 @@
import {PerigonClient as NewsSearchTool} from "@agentic/perigon";
script({
system: ["system.tools"],
tools: "agent",
maxTokens: 8192
})
const newSearchTool = new NewsSearchTool();
defTool(newSearchTool)
$`You are a chat assistant that uses agent tools to solve problems.
while true:
- ask the user for a question using the agent_user_input
- make a plan to answer the question step by step
- answer the question
end while
## guidance:
- use the agent tools to help you
- do NOT try to ask the user questions directly, use the agent_user_input tool instead.
`

View File

@@ -0,0 +1,281 @@
import {task, entrypoint, interrupt, MemorySaver} from "@langchain/langgraph"
import "./tools/searxng.genai.mjs"
import {SearxngClient} from "@agentic/searxng";
script({
title: "Deep Research Program",
description: "Researchers can use this program to conduct deep research on a topic",
model: "large",
cache: "ephemeral",
})
const {output, vars} = env
const breakdownResearch = task(
"breakdown_research",
async (question: string) => {
const result = await runPrompt(
async (ctx) => {
ctx.$`You are an expert research strategist.
Task: Break down the following research question into 3-5 focused sub-questions that would help comprehensively answer the main question.
Research question: ${question}
For each sub-question:
1. Assign a unique ID (e.g., SQ1, SQ2)
2. Explain the rationale for why this sub-question is important
3. Ensure the sub-questions collectively cover the main research question
Output the breakdown as a JSON object.`
},
{
label: "breakdown research",
responseSchema: {
type: "object",
properties: {
mainQuestion: {type: "string"},
subQuestions: {
type: "array",
items: {
type: "object",
properties: {
id: {type: "string"},
question: {type: "string"},
rationale: {type: "string"},
},
},
},
},
},
}
)
return result.json
}
)
const globalCtx = this;
const researchSubQuestion = task(
"research_subquestion",
async (subQuestion: { id: string; question: string }) => {
const searxng = new SearxngClient({apiBaseUrl: "https://search-engine-gsio.fly.dev"});
const {text} = await runPrompt(
(_) => {
_.defTool(searxng)
_.$`You are an expert researcher with access to comprehensive information.
Task: Thoroughly research the following question and provide a detailed answer.
Question ID: ${subQuestion.id}
Question: ${subQuestion.question}
Provide your findings in a structured format that includes:
- Your answer to the sub-question
- Relevant sources that support your answer
- Your confidence level in the answer (0-1)`
},
{
model: "small",
label: `research subquestion ${subQuestion.id}`,
maxDataRepairs: 2,
responseSchema: {
type: "object",
properties: {
subQuestionId: {type: "string"},
answer: {type: "string"},
sources: {
type: "array",
items: {
type: "object",
properties: {
title: {type: "string"},
url: {type: "string"},
relevance: {type: "string"},
},
},
},
confidence: {type: "number"},
},
},
}
)
return text
}
)
const synthesizeFindings = task(
"synthesize_findings",
async (mainQuestion: string, findings: any[]) => {
const result = await runPrompt(
async (ctx) => {
ctx.$`You are an expert research synthesizer.
Task: Synthesize the following research findings into a coherent response to the main research question.
Main Research Question: ${mainQuestion}
Findings:
${JSON.stringify(findings, null, 2)}
Provide a synthesis that:
1. Directly answers the main research question
2. Integrates the findings from all sub-questions
3. Identifies limitations in the current research
4. Suggests next steps for further investigation`
},
{
label: "synthesize findings",
responseType: "markdown",
responseSchema: {
type: "object",
properties: {
summary: {type: "string"},
findings: {type: "array", items: {type: "string"}},
limitations: {
type: "array",
items: {type: "string"},
},
nextSteps: {type: "array", items: {type: "string"}},
},
},
}
)
return result.json
}
)
const summarizeAndIdentifyGaps = task(
"summarize_and_identify_gaps",
async (synthesis: any, findings: any[]) => {
const result = await runPrompt(
async (ctx) => {
ctx.$`You are an expert research evaluator.
Task: Review the research synthesis and identify any gaps or areas that need deeper investigation.
Current synthesis:
${JSON.stringify(synthesis, null, 2)}
Research findings:
${JSON.stringify(findings, null, 2)}
Please provide:
1. A concise summary of current findings
2. Identify 2-3 specific knowledge gaps
3. Formulate follow-up questions to address these gaps`
},
{
label: "identify research gaps",
responseSchema: {
type: "object",
properties: {
summary: {type: "string"},
gaps: {
type: "array",
items: {type: "string"},
},
followUpQuestions: {
type: "array",
items: {
type: "object",
properties: {
id: {type: "string"},
question: {type: "string"},
},
},
},
},
},
}
)
return result.json
}
)
const researchWorkflow = entrypoint(
{checkpointer: new MemorySaver(), name: "research_workflow"},
async (input: { question: string; context?: string }) => {
const breakdown = await breakdownResearch(input.question)
const subQuestionFindings = []
for (const sq of breakdown.subQuestions) {
const analysis = await researchSubQuestion(sq);
console.log(analysis);
subQuestionFindings.push(analysis);
}
let synthesis = await synthesizeFindings(
input.question,
subQuestionFindings
)
const gapAnalysis = await summarizeAndIdentifyGaps(
synthesis,
subQuestionFindings
)
const followUpFindings = [];
for (const fq of gapAnalysis.followUpQuestions) {
const anwser = await researchSubQuestion(fq);
console.log(anwser);
followUpFindings.push(anwser);
}
const allFindings = [...subQuestionFindings, ...followUpFindings]
const finalSynthesis = await synthesizeFindings(
input.question,
allFindings
)
return {
question: input.question,
breakdown: breakdown,
initialFindings: subQuestionFindings,
gapAnalysis: gapAnalysis,
followUpFindings: followUpFindings,
synthesis: finalSynthesis,
}
}
)
const researchQuestion =
env.vars.question ||
"What are the most promising approaches to climate change mitigation?"
const threadId = `research-${Date.now()}`
const config = {
configurable: {
thread_id: threadId,
},
}
const results = await researchWorkflow.invoke(
{
question: researchQuestion,
context: vars.context || "",
},
config
)
output.fence(results, "json")

View File

@@ -0,0 +1,80 @@
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

@@ -0,0 +1,11 @@
console.log("Generating image")
def("USER_INPUT", env.vars.user_input);
const inputs = {
host: JSON.parse(env.vars.user_input).host,
imageId: JSON.parse(env.vars.user_input).imageId
}
console.log(`![Generated Image](/generated/image/${inputs.imageId})`);

View File

@@ -0,0 +1,26 @@
script({
title: "Stock Market News Scraper",
tools: ["searxng"],
})
defTool({
"mcp-server-firecrawl": {
command: "npx",
args: ["-y", "firecrawl-mcp"],
},
})
def("QUERY_NEWS", "Latest news on AAPL")
def("QUERY_SENTIMENT", "Market sentiment for technology sector")
$`Search the query with searxng: QUERY_NEWS`
$`Scrape the top search result with firecrawl`
$`Search the query with searxng: QUERY_SENTIMENT`
$`Scrape the top search result with firecrawl`

View File

@@ -0,0 +1,24 @@
import {SearxngClient} from "@agentic/searxng";
import "./tools/searxng.genai.mjs"
script({
title: "news_search_agent",
tools: ["searxng"],
maxToolCalls: 2,
cache: false,
});
def("USER_INPUT", env.vars.user_input);
def("TODAY", new Date().toISOString().split("T")[0]);
def("LINK_FORMAT", "[Link](url)");
$`You are an assistant searching for news using complex queries to pinpoint results.
- tailor search to answer the question in USER_INPUT
- perform 2 searches in parallel sorted by relevance and date respectively
- create a markdown table of <=5 results of both searches
- header row: Date, Title, Summary, and Link
Respond with a single table, no extra text.`

View File

@@ -0,0 +1,18 @@
script({
isSystem: true
})
import {SearxngClient} from "@agentic/searxng";
import ky from 'ky';
const kyWithHeaders = ky.create({
referrerPolicy: "unsafe-url",
headers: {
'Authorization': 'Basic ' + btoa(`admin:${process.env.SEARXNG_PASSWORD}`),
}
});
const searxng = new SearxngClient({ky: kyWithHeaders});
defTool(searxng)

View File

@@ -0,0 +1,88 @@
import {Window} from 'happy-dom';
import {platform} from 'os';
script({
title: "scrape",
cache: false,
});
/*
"url": "Full URL in the conversation that references the URL being interacted with. No trailing slash!",
"query": "Implied question about the resources at the URL.",
"action": "read | scrape | crawl"
*/
try {
const {url, query, action} = JSON.parse(env.vars.user_input);
} catch (e) {
throw "Sorry! Something went wrong.";
}
const {url, query, action} = JSON.parse(env.vars.user_input);
def("URL", url);
def("QUERY", query);
def("ACTION", action);
// console.log({url, query, action});
if(!(new URL(url) ?? undefined)) {
throw "Bad URL. Maybe try again?"
}
function getBrowser(): "webkit" | "chromium" | "firefox" {
if (platform() === 'darwin') {
return "webkit"; // macOS is identified by 'darwin'
}
return "chromium"; // default to chromium for other platforms
}
const {text} = await host.fetchText(new URL(url).toString());
// const browser = getBrowser();
// const page = await host.browse(new URL(url).toString(), {
// browser: getBrowser(),
// headless: true,
// javaScriptEnabled: browser !== "chromium",
// // timeout: 3000,
// // bypassCSP: true,
// // baseUrl: new URL(url).origin,
// });
//
// const html = (await page.content());
// const title = (await page.title());
// console.log({html});
const window = new Window({
// url: "http://localhost:8080",
height: 1920,
width: 1080,
settings: {
navigator: {
userAgent: 'Mozilla/5.0 (compatible; GeoffsAI/1.0; +https://geoff.seemueller.io)',
},
}
});
window.document.body.innerHTML = text;
const textContent = window.document.body.textContent;
def("PAGE_TEXT", textContent);
$`You a helpful assistant interacting with resources found at the URL.
- markdown table is concise representation of PAGE_TEXT relevant to the QUERY
### Respond Example:
### Data from ${url}:
| Header 1 | Header 2 | Header 3 |
|----------|----------|----------|
| Data 1 | Data 2 | Data 3 |
\n---[Example explanation of data significance to query.]
---
Respond with the markdown table and an explanation of significance. Do not include extra text.`;

View File

@@ -0,0 +1,28 @@
import {SearxngClient} from "@agentic/searxng";
import "./tools/searxng.genai.mjs"
script({
title: "web_search_agent",
maxTokens: 8192,
cache: false,
tools: ["searxng"],
});
def("USER_INPUT", env.vars.user_input);
def("LINK_FORMAT", "[Link](url)");
$`You are an assistant searching for web content using complex queries to pinpoint results.
- tailor search to answer the question in USER_INPUT
- perform 2 searches in parallel sorted by relevance and date respectively
- create a markdown table of <=5 results of both searches
- header row: Title, Description, and Link
Respond with a single table, no extra text.`

View File

@@ -0,0 +1,28 @@
{
"name": "@web-agent-rs/genaiscript",
"type": "module",
"workspaces": ["packages/*"],
"private": true,
"scripts": {
"dev": "cargo watch -x 'run src/main.rs'",
"ai:search": "genaiscript run genaisrc/web-search.genai.mts --vars USER_INPUT='who won the 2024 election?'",
"shim:ai:search": "pnpm build && ./dist/shim.js --file=genaisrc/search.genai.mts USER_INPUT=\"Who won the 2024 presidential election?\"\n",
"ai:news": "genaiscript run genaisrc/news-search.genai.mts --vars USER_INPUT='What are the latest updates and developments in the Ukraine war?'",
"ai:url:read": "genaiscript run genaisrc/web-scrape.genai.mts --vars USER_INPUT='{\"url\":\"https://geoff.seemueller.io/about\",\"query\":\"Describe the details of the page.\", \"action\": \"read\"}'",
"ai:url:scrape": "npx genaiscript run genaisrc/web-scrape.genai.mts --vars USER_INPUT='{\"url\":\"https://www.time4learning.com/homeschool-curriculum/high-school/eleventh-grade/math.html\",\"query\":\"What is on this page?\", \"action\": \"scrape\"}'",
"crypto:quote": "npx genaiscript run genaisrc/finance-query.genai.mts --vars USER_INPUT='Get a quote for BTC'",
"crypto:news": "npx genaiscript run genaisrc/finance-query.genai.mts --vars USER_INPUT='What is the news for Bitcoin?'",
"crypto:overview": "npx genaiscript run genaisrc/finance-query.genai.mts --vars USER_INPUT='What are the trending symbols in the market?'"
},
"dependencies": {
"@agentic/perigon": "^7.2.0",
"@agentic/searxng": "7.5.3",
"@kevinwatt/mcp-server-searxng": "^0.3.9",
"@types/node": "^22.10.2",
"genaiscript": "^1.95.1",
"happy-dom": "^16.0.1",
"@web-agent-rs/perigon": "workspace:*",
"@web-agent-rs/core": "workspace:*",
"ky": "^1.8.0"
}
}