chat-agent-toolkit 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +234 -0
  2. package/package.json +101 -0
  3. package/src/config/config-manager.ts +319 -0
  4. package/src/config/config-types.ts +65 -0
  5. package/src/config/environment-variables.ts +8 -0
  6. package/src/config/index.ts +26 -0
  7. package/src/config/language-models-database.ts +1426 -0
  8. package/src/config/mcp-server-registry.ts +24 -0
  9. package/src/config/model-registry.ts +256 -0
  10. package/src/config/model-tester.ts +211 -0
  11. package/src/config/model-utils.ts +193 -0
  12. package/src/config/provider-ui-config.ts +196 -0
  13. package/src/connectors/composio.json +154 -0
  14. package/src/index.ts +22 -0
  15. package/src/memory/ARCHITECTURE.md +302 -0
  16. package/src/memory/MASTRA_INTEGRATION.md +106 -0
  17. package/src/memory/README.md +224 -0
  18. package/src/memory/agent-memory-manager.ts +416 -0
  19. package/src/memory/example.ts +343 -0
  20. package/src/memory/index.ts +47 -0
  21. package/src/memory/mastra-integration.ts +609 -0
  22. package/src/memory/storage/drizzle-storage.ts +205 -0
  23. package/src/memory/storage/in-memory-storage.ts +551 -0
  24. package/src/memory/storage/storage-interface.ts +68 -0
  25. package/src/memory/types.ts +125 -0
  26. package/src/models/providers.ts +5 -0
  27. package/src/models/registry.ts +4 -0
  28. package/src/models/types.ts +4 -0
  29. package/src/search.ts +5 -0
  30. package/src/tools/composio-mastra.ts +305 -0
  31. package/src/tools/composio-mcp.ts +205 -0
  32. package/src/tools/index.ts +13 -0
  33. package/src/tools/qwksearch-api-tools.ts +327 -0
  34. package/src/tools/search/doc-utils.ts +75 -0
  35. package/src/tools/search/document.ts +47 -0
  36. package/src/tools/search/index.ts +13 -0
  37. package/src/tools/search/link-summarizer.ts +112 -0
  38. package/src/tools/search/meta-search-types.ts +62 -0
  39. package/src/tools/search/metaSearchAgent.ts +454 -0
  40. package/src/tools/search/search-handlers.ts +85 -0
  41. package/src/tools/search/suggestionGeneratorAgent.ts +54 -0
  42. package/src/types.d.ts +137 -0
  43. package/src/utils/README.md +98 -0
  44. package/src/utils/chat-helpers.ts +18 -0
  45. package/src/utils/index.ts +2 -0
  46. package/src/utils/markdown-to-html.ts +53 -0
  47. package/src/utils/outputParser.ts +73 -0
  48. package/src/utils/provider-image-cropper.ts +130 -0
package/README.md ADDED
@@ -0,0 +1,234 @@
1
+ # agent-toolkit
2
+
3
+
4
+ Multi-provider AI agent toolkit for generating language responses, searching the web, extracting page content, and managing long-term memory across 10+ LLM providers.
5
+
6
+ Built on top of the [Vercel AI SDK](https://sdk.vercel.ai), with a small registry of pre-tuned agent prompts (research, summarization, citation answering, query resolution, knowledge-graph extraction, etc.) and tool wrappers around the [QwkSearch](https://qwksearch.com) API.
7
+
8
+ ## Language Intelligence Providers
9
+
10
+ | Provider | 🌍 | Top Model (Others) | 🏆 Benchmarks | 📄 Docs | 🔑 Keys | 💰 Funding |
11
+ | ---------------------- | --- | --------------------------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | ------------ |
12
+ | **Anthropic** | 🇺🇸 | Claude Mythos / Opus  (Sonnet, Haiku) | 🥇 GPQA Diamond 94.6% · 🥇 SWE-bench 93.9% · 🧬 PhD reasoning | [Docs](https://docs.anthropic.com/en/docs/welcome) | [Keys](https://console.anthropic.com/settings/keys) | ~$60B |
13
+ | **OpenAI** | 🇺🇸 | GPT / o3 / Codex (o1, o4, o4-mini, gpt-4o) | 🥇 AIME 2025 100% · 🥇 SWE-bench Pro · 📚 MMLU-Pro 90% | [Docs](https://platform.openai.com/docs/overview) | [Keys](https://platform.openai.com/api-keys) | ~$180B |
14
+ | **Google** | 🇺🇸 | Gemini Pro (Flash, Flash-Lite, Gemma) | 🥇 GPQA 94.1% · 🥇 LiveCodeBench Elo 2439 · 🌐 #1 in 6/13 Vals | [Docs](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models) | [Keys](https://cloud.google.com/vertex-ai/generative-ai/docs/start/express-mode/overview#api-keys) | Public |
15
+ | **xAI** | 🇺🇸 | Grok Heavy (Grok-3, Grok Vision) | 🥇 AIME 2025 100% · 🧮 Math competition · ⚡ X integration | [Docs](https://docs.x.ai/docs#models) | [Keys](https://console.x.ai/) | ~$45B |
16
+ | **Meta** | 🇺🇸 | Llama Maverick / Scout (Llama 3.x, CodeLlama) | 🥇 DocVQA 94.4% · 🥇 10M token context · 📊 ChartQA 90% | [Docs](https://www.llama.com/docs/overview/) | [Keys](https://www.llama.com/llama-downloads/) | Public |
17
+ | **NVIDIA** | 🇺🇸 | Nemotron-Cascade  (Llama Nemotron, Kimi) | 🥇 LCB v6 87.2% · 🏅 IMO+IOI+ICPC gold · 🧮 AIME 98.6% | [Docs](https://docs.api.nvidia.com/nim/reference/llm-apis) | [Keys](https://build.nvidia.com/settings/api-keys) | Public |
18
+ | **Perplexity** | 🇺🇸 | Sonar Reasoning Pro (Sonar Deep Research) | 🥇 Search Arena · 🔍 #1 web-grounded QA · 🌐 Real-time retrieval | [Docs](https://docs.perplexity.ai/models/model-cards) | [Keys](https://www.perplexity.ai/account/api/keys) | ~$1B |
19
+ | **Groq** | 🇺🇸 | (Llama, DeepSeek, Gemma, Mistral, Qwen) | ⚡ #1 inference speed · 🏎️ Fastest TTFT · 🔧 LPU hardware | [Docs](https://console.groq.com/docs/overview) | [Keys](https://console.groq.com/keys) | ~$640M |
20
+ | **Mistral** | 🇫🇷 | Mistral Large  (Small 4, Codestral, Devstral) | 🥈 Arena Elo 1418 · 🌍 Multilingual MMLU 85.5% · 🚀 Fastest TTFT | [Docs](https://docs.mistral.ai/) | [Keys](https://console.mistral.ai/api-keys/) | ~$3.1B |
21
+ | **Together** | 🇺🇸 | (Llama, Mistral, Gemma, Qwen, DeepSeek) | 🏗️ Widest open hosting · 💸 Best open-source pricing · 🔧 Fine-tuning | [Docs](https://docs.together.ai/docs/quickstart) | [Keys](https://api.together.xyz/settings/api-keys) | ~$225M |
22
+ | **Moonshot** | 🇨🇳 | Kimi Reasoning (K2.6, K2) | 🥇 AIME open 96.1% · 🥇 MATH-500 98% · 🥇 HumanEval 99% | [Docs](https://platform.moonshot.cn/docs) | [Keys](https://platform.moonshot.cn/console/api-keys) | ~$3.9B |
23
+ | **Zhipu** | 🇨🇳 | GLM Reasoning / GLM-4.7 (GLM-4V, CogView) | 🥇 Chatbot Arena Elo 1451 · 🥇 MMLU 96% · 🧮 AIME 95.7% | [Docs](https://bigmodel.cn/dev/api) | [Keys](https://bigmodel.cn/usercenter/apikeys) | ~$1.8B |
24
+ | **Alibaba** | 🇨🇳 | Qwen-Coder / Qwen  (Qwen-VL, Qwen-Audio) | 🥇 Codeforces Elo 2056 · 💻 SWE-bench 69.6% · 🏎️ LCB 70.7% | [Docs](https://www.alibabacloud.com/help/en/model-studio/developer-reference/use-qwen-by-calling-api) | [Keys](https://bailian.console.aliyun.com/?apiKey=1) | Public |
25
+ | **DeepSeek** | 🇨🇳 | DeepSeek (DeepSeek-Coder, DeepSeek-VL) | 🥇 IMO gold (open) · 📚 MMLU-Pro 81.2 · 🧮 AIME 87.5% | [Docs](https://api-docs.deepseek.com/) | [Keys](https://platform.deepseek.com/api_keys) | Bootstrapped |
26
+ | **Cloudflare** | 🇺🇸 | (Llama, Mistral, Gemma, Qwen, DeepSeek) | 🌐 Edge inference · ⚡ Serverless CDN scale · 🔒 Privacy-first | [Docs](https://developers.cloudflare.com/workers-ai/) | [Keys](https://dash.cloudflare.com/profile/api-tokens) | Public |
27
+ | **Ollama** | 🇺🇸 | (Llama, Mistral, Gemma, Qwen, DeepSeek) | 🖥️ #1 local inference · 🔒 Fully offline · 🆓 Free self-hosted | [Docs](https://ollama.com/docs) | [Keys](https://ollama.com/settings/keys) | ~$20M |
28
+
29
+ ## Amazon Bedrock Provider
30
+
31
+ Use Vercel AI SDK's official Bedrock provider: install `@ai-sdk/amazon-bedrock`, configure AWS auth, then call `bedrock('model-id')` in `generateText` or `streamText`. [ai-sdk](https://ai-sdk.dev/providers/ai-sdk-providers/amazon-bedrock)
32
+
33
+ ### Install
34
+
35
+ - Run `pnpm add ai @ai-sdk/amazon-bedrock` or the npm/yarn equivalent. [vercel](https://vercel.com/changelog/amazon-bedrock-provider-for-the-vercel-ai-sdk-now-available)
36
+ - Import either `bedrock` directly or `createAmazonBedrock` for custom config. [ai-sdk](https://ai-sdk.dev/providers/ai-sdk-providers/amazon-bedrock)
37
+
38
+ ### Env setup
39
+
40
+ - Simplest path: set `AWS_BEARER_TOKEN_BEDROCK` with a Bedrock API key; the docs say API key auth is the recommended method. [ai-sdk](https://ai-sdk.dev/providers/ai-sdk-providers/amazon-bedrock)
41
+ - SigV4 also works with `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_REGION`, and the provider can also use the AWS credential chain. [ai-sdk](https://ai-sdk.dev/providers/ai-sdk-providers/amazon-bedrock)
42
+
43
+ ### Minimal example
44
+
45
+ ```ts
46
+ import { generateText } from 'ai';
47
+ import { bedrock } from '@ai-sdk/amazon-bedrock';
48
+
49
+ const { text } = await generateText({
50
+ model: bedrock('anthropic.claude-3-haiku-20240307-v1:0'),
51
+ prompt: 'Explain Bedrock in one sentence.',
52
+ });
53
+
54
+ console.log(text);
55
+ ```
56
+
57
+ ### Custom region
58
+
59
+ ```ts
60
+ import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock';
61
+
62
+ export const amazonBedrock = createAmazonBedrock({
63
+ region: 'us-east-1',
64
+ apiKey: process.env.AWS_BEARER_TOKEN_BEDROCK,
65
+ });
66
+ ```
67
+
68
+ ### Notes
69
+
70
+ - You must enable model access in the AWS Bedrock console first; access is not granted by default. [ai-sdk](https://ai-sdk.dev/providers/ai-sdk-providers/amazon-bedrock)
71
+ - `streamText` is supported too, and Bedrock-specific options like guardrails can be passed via `providerOptions.bedrock`. [ai-sdk](https://ai-sdk.dev/providers/ai-sdk-providers/amazon-bedrock)
72
+
73
+ ## Install
74
+
75
+ ```bash
76
+ npm install ai-research-agent
77
+ ```
78
+
79
+ ## Quick start
80
+
81
+ ```ts
82
+ import { generateLanguageResponse } from "ai-research-agent";
83
+
84
+ const response = await generateLanguageResponse({
85
+ provider: "groq",
86
+ apiKey: process.env.GROQ_API_KEY,
87
+ agent: "question",
88
+ query: "Explain transformer attention in two sentences.",
89
+ });
90
+
91
+ console.log(response.content);
92
+ ```
93
+
94
+ `response.content` is HTML by default (set `html: false` for raw Markdown). Agents that define an `after` hook also return parsed data on `response.extract`.
95
+
96
+ ### Using Amazon Bedrock
97
+
98
+ ```ts
99
+ import { generateLanguageResponse } from "ai-research-agent";
100
+
101
+ const response = await generateLanguageResponse({
102
+ provider: "amazon",
103
+ apiKey: process.env.AWS_BEARER_TOKEN_BEDROCK, // or "region:accessKeyId:secretAccessKey"
104
+ model: "anthropic.claude-3-5-sonnet-20241022-v2:0",
105
+ agent: "question",
106
+ query: "Explain transformer attention in two sentences.",
107
+ });
108
+
109
+ console.log(response.content);
110
+ ```
111
+
112
+ ## Supported providers
113
+
114
+ | Provider | ID | Default model | Notes |
115
+ | --------------- | ------------ | ----------------------------------------------- | ------------------------------------------------- |
116
+ | Anthropic | `anthropic` | `claude-3-7-sonnet-20250219` | |
117
+ | OpenAI | `openai` | `gpt-4o` | |
118
+ | Groq | `groq` | `meta-llama/llama-4-maverick-17b-128e-instruct` | |
119
+ | Google Vertex | `google` | Gemini 2.x | |
120
+ | XAI | `xai` | `grok-beta` | |
121
+ | Amazon Bedrock | `amazon` | `anthropic.claude-3-5-sonnet-20241022-v2:0` | apiKey = bearer token or `region:key:secret` |
122
+ | Cloudflare | `cloudflare` | `llama-4-scout-17b-16e-instruct` | apiKey =`token:accountId` |
123
+ | Together AI | `togetherai` | `meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo` | |
124
+ | Perplexity | `perplexity` | `sonar` | |
125
+ | NVIDIA NIM | `nvidia` | `moonshotai/kimi-k2.5` | |
126
+ | Ollama | `ollama` | `llama3.2` | Local — no key required |
127
+
128
+ The full registry (including context lengths) is exported as `LANGUAGE_MODELS`.
129
+
130
+ ## Built-in agents
131
+
132
+ The `agent` option selects a prompt template from [`AGENT_PROMPTS`](src/agents/agent-prompts.ts):
133
+
134
+ - `question` — general-purpose Q&A with chat history.
135
+ - `question-research-engine` — long-form, journalistic, citation-style answer.
136
+ - `query-resolution` — rephrases a follow-up question into standalone search queries.
137
+ - `query-resolution-search` — classifies a query and proposes search categories as JSON.
138
+ - `summarize`, `summarize-bullets`, `summary-longtext` — single-shot and reduce-style summarization.
139
+ - `answer-cite-sources` — answers a query against a `{context}` block with `[n]` citations.
140
+ - `suggest-followups` — emits 4–5 follow-up questions inside `<suggestions>` tags.
141
+ - `remember-user` — extracts factual user memories as structured JSON.
142
+ - `knowledge-graph-nodes` — builds a temporal knowledge graph from a document.
143
+ - `results-relevance-filter` — picks the most relevant URLs from a search result list.
144
+
145
+ Templates use `{variableName}` placeholders that are filled from the options object (`query`, `article`, `chat_history`, `context`, etc.).
146
+
147
+ ## Agent tools
148
+
149
+ `AGENT_TOOLS` registers callable tools that the ReAct agent attaches automatically when a prompt declares them in its `tools` array:
150
+
151
+ - `web_search` — QwkSearch metasearch over 100+ engines.
152
+ - `extract_page` — Mozilla Readability + Mercury extraction with PDF and YouTube support.
153
+ - `generate_ai_response` — proxy to QwkSearch's hosted `/language` endpoint.
154
+
155
+ Set `QWKSEARCH_URL` and `QWKSEARCH_API_KEY` in the environment, or pass `baseURL`/`apiKey` per-call.
156
+
157
+ ## Memory
158
+
159
+ ```ts
160
+ import {
161
+ SimpleMemory,
162
+ MemoryAgent,
163
+ DrizzleMemoryStorage,
164
+ } from "ai-research-agent";
165
+ ```
166
+
167
+ - `SimpleMemory` — in-memory store implementing `IMemoryStorage`.
168
+ - `DrizzleMemoryStorage` — Drizzle ORM-backed persistence; pair with `createMemorySchema()`.
169
+ - `MemoryAgent` — high-level wrapper that uses the `remember-user` agent to extract facts from chat turns and writes them to a storage backend.
170
+
171
+ See [src/memory/README.md](src/memory/README.md) and [src/memory/ARCHITECTURE.md](src/memory/ARCHITECTURE.md) for details.
172
+
173
+ ## Package layout
174
+
175
+ ```
176
+ src/
177
+ agents/ # prompt + tool registry, generate function, model list
178
+ agent-prompts.ts # AGENT_PROMPTS, extractJSONFromLanguageReply
179
+ agent-tools.ts # AGENT_TOOLS (web_search, extract_page, ...)
180
+ generate-language.ts # generateLanguageResponse — main entry point
181
+ generate-language-types.ts
182
+ language-model-names.ts # LANGUAGE_MODELS, LANGUAGE_PROVIDERS
183
+ llm-providers.ts # createLLMProvider — chat-model factory
184
+ index.ts # barrel export
185
+ memory/ # SimpleMemory, MemoryAgent, Drizzle storage
186
+ providers/ # alternate provider abstractions (model registry)
187
+ utils/ # markdown-to-html, document helpers
188
+ index.ts # public entry — re-exports agents + memory
189
+ ```
190
+
191
+ ## Build
192
+
193
+ ```bash
194
+ npm run build # vite build
195
+ npm run make # clean + build with extra heap
196
+ npm run test # vitest
197
+ ```
198
+
199
+ Vite bundles ES + CJS targets to `dist/`, emits `.d.ts` files alongside, and applies a `fs/promises` polyfill so the bundle works in browser and edge runtimes (Cloudflare Workers, Vercel Edge).
200
+
201
+ ## Resources
202
+
203
+ ### Documentation & Guides
204
+ - [Vercel AI SDK generateText docs](https://sdk.vercel.ai/docs/reference/ai-sdk-core/generate-text)
205
+ - [Hugging Face tutorials](https://huggingface.co/learn)
206
+ - [Illustrated Transformer](https://jalammar.github.io/illustrated-transformer/)
207
+ - [Building a Transformer with PyTorch](https://www.datacamp.com/tutorial/building-a-transformer-with-py-torch)
208
+ - [LLM training example](https://github.com/vtempest/ai-research-agent/blob/master/packages/neural-net/src/train/predict-next-word.js)
209
+
210
+ ### Transformer Architecture Visualizations
211
+
212
+ <img src="https://i.imgur.com/uW6E9VJ.gif" alt="Transformer architecture visualization" />
213
+
214
+
215
+ ## Alternative Agents Frameworks
216
+
217
+ | # | Framework | Features | Stars | Language | Maker |
218
+ |---:|---|---|---:|---|---|
219
+ | 1 | [Hermes Agent](https://github.com/NousResearch/hermes-agent) | Self-improving agent with a built-in learning loop; distills reusable skills from past runs, persists experience across sessions, "grows with you" over time | 211k | Python | Nous Research |
220
+ | 2 | [AutoGPT](https://github.com/Significant-Gravitas/AutoGPT) | One of the original autonomous agents; goal-driven loop that self-prompts to decompose and execute tasks. Now a low-code platform: visual block-based builder, deploy/run continuous agents, and an agent marketplace | 185k | Python/TypeScript | Significant Gravitas |
221
+ | 3 | [n8n](https://github.com/n8n-io/n8n) | Workflow automation, native AI features, 400+ integrations, visual/code hybrid | 175k | TypeScript | n8n.io |
222
+ | 4 | [LangChain / LangGraph](https://github.com/langchain-ai/langchain) | Full ecosystem: LangChain for chains/model+tool abstractions, [LangGraph](https://github.com/langchain-ai/langgraph) (36.7k) for graph-native orchestration with explicit state, branching, loops, checkpoints, human-in-the-loop, durable execution, streaming; [LangGraphJS](https://github.com/langchain-ai/langgraphjs) (3.1k) mirrors it for JS runtimes | 141k | Python + TS/JS | LangChain AI |
223
+ | 5 | [Dify](https://github.com/langgenius/dify) | Visual workflow builder, RAG pipelines, agent apps, deployment tooling | 130k–147k+ | TypeScript | LangGenius |
224
+ | 6 | [OpenHands](https://github.com/All-Hands-AI/OpenHands) | Autonomous dev agent — writes/edits code, runs shell commands, browses the web, executes in a sandbox; strong local secure-workflow story | 80k | Python | All-Hands-AI (formerly OpenDevin) |
225
+ | 7 | [MetaGPT](https://github.com/FoundationAgents/MetaGPT) | Simulates a software company (PM/architect/dev/QA roles), SOP-driven pipelines, generates specs, docs, and code from one prompt | 69k | Python | Foundation Agents |
226
+ | 8 | [Mem0](https://github.com/mem0ai/mem0) | Memory layer, not an orchestrator: extraction, consolidation, scoped retrieval, long-term user/agent memory; drops into any framework | 60k | Python/TypeScript | mem0ai |
227
+ | 9 | [AutoGen](https://github.com/microsoft/autogen) | Dialogue-first multi-agent conversations, async event-driven messaging, distributed collaboration, observability, code execution | 60k | Python | Microsoft |
228
+ | 10 | [CrewAI](https://github.com/crewAIInc/crewAI) | Role-based "crews," task delegation and collaboration, memory, checkpointing, async flows; lean (no LangChain dependency) | 55k | Python | CrewAI Inc. / João Moura |
229
+ | 11 | [LlamaIndex](https://github.com/run-llama/llama_index) | Retrieval-centric agents, event-driven Workflows, RAG-first data connectors, memory, structured query planning | 51k | Python | LlamaIndex |
230
+ | 12 | [smolagents](https://github.com/huggingface/smolagents) | Minimal code-first agents that write actions as Python code, sandboxed execution, tiny surface area, model-agnostic | 28k | Python | Hugging Face |
231
+ | 13 | [OpenAI Agents SDK](https://github.com/openai/openai-agents-python) | Lightweight agent loop, handoffs between agents, guardrails, sessions, built-in tracing, sandboxed tool execution | 28k | Python/TypeScript | OpenAI |
232
+ | 14 | [Mastra](https://github.com/mastra-ai/mastra) | TS-native, batteries-included: agents, graph-based workflows (`.then()`/`.branch()`/`.parallel()`), 40+ model routing, observational memory, human-in-the-loop, authored MCP servers, built-in evals + observability; deploys standalone or inside React/Next/Node | 26k | TypeScript | Mastra (mastra.ai) |
233
+ | 15 | [Vercel AI SDK](https://github.com/vercel/ai) | Streaming, tool calling, structured outputs, agent loops, MCP support, provider-agnostic, first-class UI hooks; runs on edge/Workers | 25k | TypeScript | Vercel |
234
+ | 16 | [Zep](https://github.com/getzep/zep) | Agent memory platform built on a temporal knowledge graph; long-term context, fact extraction, cross-session recall | 4.7k | Python/TypeScript | getzep |
package/package.json ADDED
@@ -0,0 +1,101 @@
1
+ {
2
+ "name": "chat-agent-toolkit",
3
+ "version": "1.2.1",
4
+ "description": "Multi-provider AI agent toolkit: generate language responses, search the web, extract content, and manage memory across 10+ LLM providers.",
5
+ "author": "vtempest <grokthiscontact@gmail.com>",
6
+ "license": "AGPL-3.0",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/OpenSourceAGI/qwksearch-research-agent"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/OpenSourceAGI/qwksearch-research-agent/issues"
13
+ },
14
+ "main": "./dist/research-agent.cjs.js",
15
+ "types": "./dist/types.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/types.d.ts",
19
+ "import": "./dist/research-agent.es.js",
20
+ "require": "./dist/research-agent.cjs.js"
21
+ },
22
+ "./*": {
23
+ "types": "./src/*.ts",
24
+ "react-server": "./src/*",
25
+ "import": "./src/*",
26
+ "require": "./src/*"
27
+ }
28
+ },
29
+ "typesVersions": {
30
+ "*": {
31
+ "*": [
32
+ "src/*"
33
+ ]
34
+ }
35
+ },
36
+ "files": [
37
+ "dist",
38
+ "src"
39
+ ],
40
+ "sideEffects": false,
41
+ "scripts": {
42
+ "build": "vite build",
43
+ "test": "vitest",
44
+ "test-ui": "vitest --ui --watch",
45
+ "make": "rm -rf dist/*; NODE_OPTIONS=--max-old-space-size=15192 vite build",
46
+ "ship": "npx standard-version --release-as patch && npm publish --access public"
47
+ },
48
+ "peerDependencies": {
49
+ "next": ">=15.0.0",
50
+ "vinext": ">=0.1.0 || >=1.0.0-0"
51
+ },
52
+ "peerDependenciesMeta": {
53
+ "next": {
54
+ "optional": true
55
+ },
56
+ "vinext": {
57
+ "optional": true
58
+ }
59
+ },
60
+ "devDependencies": {
61
+ "@vitest/ui": "^4.0.18",
62
+ "terser": "^5.46.0",
63
+ "typedoc-plugin-markdown": "^4.10.0",
64
+ "typescript": "^5.9.3",
65
+ "vite": "^8.1.3",
66
+ "vite-plugin-dts": "^5.0.3",
67
+ "vite-plugin-node-polyfills": "^0.28.0",
68
+ "vitest": "^4.0.18"
69
+ },
70
+ "dependencies": {
71
+ "@ai-sdk/anthropic": "^2.0.0",
72
+ "@ai-sdk/google": "^2.0.0",
73
+ "@ai-sdk/groq": "^2.0.0",
74
+ "@ai-sdk/openai": "^2.0.0",
75
+ "@composio/core": "^0.10.0",
76
+ "@mastra/core": "^0.1.0",
77
+ "@mastra/mcp": "^0.1.0",
78
+ "ai": "^5.0.0",
79
+ "drizzle-orm": "^0.45.1",
80
+ "qwksearch-api-client": "^0.0.12",
81
+ "zod": "^4.3.6"
82
+ },
83
+ "keywords": [
84
+ "ai-agent",
85
+ "llm",
86
+ "research-agent",
87
+ "vercel-ai-sdk",
88
+ "nlp",
89
+ "web-search",
90
+ "knowledge-graph",
91
+ "multi-provider",
92
+ "groq",
93
+ "openai",
94
+ "anthropic",
95
+ "ollama",
96
+ "hacktoberfest"
97
+ ],
98
+ "publishConfig": {
99
+ "access": "public"
100
+ }
101
+ }
@@ -0,0 +1,319 @@
1
+ /**
2
+ * @fileoverview Configuration Manager
3
+ *
4
+ * Manages model providers, MCP servers, and search configuration in memory.
5
+ * Handles environment variable loading, provider hashing, and config updates.
6
+ */
7
+ import type { ConfigModelProvider, MCPServerConfig, Config, UIConfigSections, Model } from "./config-types";
8
+ import { getModelProvidersUIConfigSection } from "./provider-ui-config";
9
+ import { getEnv } from "./environment-variables";
10
+ import { LANGUAGE_MODELS } from "./language-models-database";
11
+
12
+ // Maps a provider UI key (from provider-ui-config) to the matching
13
+ // `provider` name in the LANGUAGE_MODELS database. Names differ in a few
14
+ // cases (e.g. the "gemini" UI key corresponds to the "Google" model list).
15
+ const PROVIDER_KEY_TO_DB_NAME: Record<string, string> = {
16
+ openai: "openai",
17
+ anthropic: "anthropic",
18
+ gemini: "google",
19
+ groq: "groq",
20
+ deepseek: "deepseek",
21
+ nvidia: "nvidia",
22
+ openrouter: "openrouter",
23
+ };
24
+
25
+ /**
26
+ * Returns the default chat models for a provider from the LANGUAGE_MODELS
27
+ * database so that env-based providers expose a usable model list instead of
28
+ * an empty one. Returns [] when no matching provider list exists.
29
+ */
30
+ const getDefaultChatModels = (providerKey: string): Model[] => {
31
+ const dbName = PROVIDER_KEY_TO_DB_NAME[providerKey] ?? providerKey;
32
+ const entry = LANGUAGE_MODELS.find(
33
+ (p) => p.provider.toLowerCase() === dbName.toLowerCase(),
34
+ );
35
+ if (!entry?.models) return [];
36
+
37
+ return entry.models.map((m: any) => ({
38
+ name: m.name,
39
+ key: m.id,
40
+ }));
41
+ };
42
+
43
+ const hashObj = (obj: { [key: string]: any }) => {
44
+ const str = JSON.stringify(obj, Object.keys(obj).sort());
45
+ let hash = 0;
46
+ for (let i = 0; i < str.length; i++) {
47
+ hash = (hash << 5) - hash + str.charCodeAt(i);
48
+ hash |= 0;
49
+ }
50
+ return String(Math.abs(hash).toString(36));
51
+ };
52
+
53
+ class ConfigManager {
54
+ configVersion = 1;
55
+ currentConfig: Config = {
56
+ version: this.configVersion,
57
+ setupComplete: getEnv("SETUP_COMPLETE") === "true" || false,
58
+ preferences: {},
59
+ personalization: {},
60
+ modelProviders: [],
61
+ mcpServers: [],
62
+ search: {
63
+ searxngURL: "",
64
+ tavilyApiKey: "",
65
+ sourceScrapeCount: 3,
66
+ sourceScrapeTimeout: 5,
67
+ },
68
+ };
69
+ uiConfigSections: UIConfigSections = {
70
+ preferences: [],
71
+ personalization: [],
72
+ modelProviders: [],
73
+ mcpServers: [],
74
+ search: [
75
+ {
76
+ name: "SearXNG URL",
77
+ key: "searxngURL",
78
+ type: "string",
79
+ required: false,
80
+ description: "The URL of your SearXNG instance",
81
+ placeholder: "http://localhost:4000",
82
+ default: "",
83
+ scope: "server",
84
+ env: "SEARXNG_API_URL",
85
+ },
86
+ {
87
+ name: "Tavily API Key",
88
+ key: "tavilyApiKey",
89
+ type: "string",
90
+ required: false,
91
+ description: "Your Tavily API key for enhanced search capabilities.",
92
+ placeholder: "tvly-...",
93
+ default: "",
94
+ scope: "server",
95
+ env: "TAVILY_API_KEY",
96
+ },
97
+ {
98
+ name: "Source pages to scrape",
99
+ key: "sourceScrapeCount",
100
+ type: "select",
101
+ options: [
102
+ { name: "Disabled (snippet only)", value: "0" },
103
+ { name: "1 page", value: "1" },
104
+ { name: "2 pages", value: "2" },
105
+ { name: "3 pages (default)", value: "3" },
106
+ { name: "5 pages", value: "5" },
107
+ ],
108
+ required: false,
109
+ description: "Number of top search result URLs to fully scrape.",
110
+ default: "3",
111
+ scope: "server",
112
+ },
113
+ {
114
+ name: "Scrape timeout (seconds)",
115
+ key: "sourceScrapeTimeout",
116
+ type: "select",
117
+ options: [
118
+ { name: "3 seconds", value: "3" },
119
+ { name: "5 seconds (default)", value: "5" },
120
+ { name: "10 seconds", value: "10" },
121
+ { name: "15 seconds", value: "15" },
122
+ { name: "20 seconds", value: "20" },
123
+ ],
124
+ required: false,
125
+ description: "Maximum time to wait when scraping each source URL.",
126
+ default: "5",
127
+ scope: "server",
128
+ },
129
+ ],
130
+ };
131
+
132
+ private initialized = false;
133
+
134
+ constructor() {
135
+ // Don't initialize in constructor to avoid circular dependency
136
+ // Initialize lazily when config is first accessed
137
+ }
138
+
139
+ private ensureInitialized() {
140
+ if (this.initialized) return;
141
+ this.initialize();
142
+ this.initialized = true;
143
+ }
144
+
145
+ private initialize() {
146
+ const providerConfigSections = getModelProvidersUIConfigSection();
147
+ this.uiConfigSections.modelProviders = providerConfigSections;
148
+
149
+ const newProviders: ConfigModelProvider[] = [];
150
+
151
+ // Check if OPENROUTER_API_KEY exists - prioritize OpenRouter for guests
152
+ const hasOpenRouter = !!getEnv("OPENROUTER_API_KEY");
153
+
154
+ providerConfigSections.forEach((provider) => {
155
+ // If OpenRouter is configured, ONLY load OpenRouter for guests
156
+ // Skip all other providers (NVIDIA, DeepSeek, etc.)
157
+ if (hasOpenRouter && provider.key.toLowerCase() !== "openrouter") {
158
+ return;
159
+ }
160
+
161
+ const tempConfig: Record<string, any> = {};
162
+ const required: string[] = [];
163
+
164
+ provider.fields.forEach((field) => {
165
+ tempConfig[field.key] =
166
+ getEnv(field.env!) || field.default || "";
167
+ if (field.required) required.push(field.key);
168
+ });
169
+
170
+ let configured = true;
171
+ required.forEach((r) => {
172
+ if (!tempConfig[r]) configured = false;
173
+ });
174
+
175
+ if (configured) {
176
+ const hash = hashObj(tempConfig);
177
+ const exists = this.currentConfig.modelProviders.find(
178
+ (p) => p.hash === hash,
179
+ );
180
+
181
+ if (!exists) {
182
+ newProviders.push({
183
+ id: hash,
184
+ name: `${provider.name}`,
185
+ type: provider.key,
186
+ chatModels: getDefaultChatModels(provider.key),
187
+ config: tempConfig,
188
+ hash: hash,
189
+ isEnvBased: true,
190
+ });
191
+ }
192
+ }
193
+ });
194
+
195
+ if (newProviders.length > 0) {
196
+ this.currentConfig.modelProviders.push(...newProviders);
197
+ }
198
+
199
+ // Search config from env
200
+ this.uiConfigSections.search.forEach((f) => {
201
+ if (f.env && !this.currentConfig.search[f.key]) {
202
+ this.currentConfig.search[f.key] =
203
+ getEnv(f.env) ?? f.default ?? "";
204
+ }
205
+ });
206
+ }
207
+
208
+ public getConfig(key: string, defaultValue?: any): any {
209
+ this.ensureInitialized();
210
+ const nested = key.split(".");
211
+ let obj: any = this.currentConfig;
212
+
213
+ for (let i = 0; i < nested.length; i++) {
214
+ const part = nested[i];
215
+ if (obj == null) return defaultValue;
216
+ obj = obj[part];
217
+ }
218
+
219
+ return obj === undefined ? defaultValue : obj;
220
+ }
221
+
222
+ public updateConfig(key: string, val: any) {
223
+ const parts = key.split(".");
224
+ if (parts.length === 0) return;
225
+
226
+ let target: any = this.currentConfig;
227
+ for (let i = 0; i < parts.length - 1; i++) {
228
+ const part = parts[i];
229
+ if (target[part] === null || typeof target[part] !== "object") {
230
+ target[part] = {};
231
+ }
232
+ target = target[part];
233
+ }
234
+
235
+ const finalKey = parts[parts.length - 1];
236
+ target[finalKey] = val;
237
+ }
238
+
239
+ public addModelProvider(type: string, name: string, config: any) {
240
+ this.ensureInitialized();
241
+ const hash = hashObj(config);
242
+
243
+ const newModelProvider: ConfigModelProvider = {
244
+ id: hash,
245
+ name,
246
+ type,
247
+ config,
248
+ chatModels: [],
249
+ hash: hash,
250
+ };
251
+
252
+ this.currentConfig.modelProviders.push(newModelProvider);
253
+ return newModelProvider;
254
+ }
255
+
256
+ public removeModelProvider(id: string) {
257
+ this.currentConfig.modelProviders =
258
+ this.currentConfig.modelProviders.filter((p) => p.id !== id);
259
+ }
260
+
261
+ public async updateModelProvider(id: string, name: string, config: any) {
262
+ const provider = this.currentConfig.modelProviders.find(
263
+ (p) => p.id === id,
264
+ );
265
+ if (!provider) throw new Error("Provider not found");
266
+
267
+ provider.name = name;
268
+ provider.config = config;
269
+ return provider;
270
+ }
271
+
272
+ public addProviderModel(providerId: string, type: "chat", model: any) {
273
+ const provider = this.currentConfig.modelProviders.find(
274
+ (p) => p.id === providerId,
275
+ );
276
+ if (!provider) throw new Error("Invalid provider id");
277
+
278
+ delete model.type;
279
+ provider.chatModels.push(model);
280
+ return model;
281
+ }
282
+
283
+ public removeProviderModel(
284
+ providerId: string,
285
+ type: "chat",
286
+ modelKey: string,
287
+ ) {
288
+ const provider = this.currentConfig.modelProviders.find(
289
+ (p) => p.id === providerId,
290
+ );
291
+ if (!provider) throw new Error("Invalid provider id");
292
+
293
+ provider.chatModels = provider.chatModels.filter((m) => m.key !== modelKey);
294
+ }
295
+
296
+ public isSetupComplete() {
297
+ return this.currentConfig.setupComplete;
298
+ }
299
+
300
+ public markSetupComplete() {
301
+ if (!this.currentConfig.setupComplete) {
302
+ this.currentConfig.setupComplete = true;
303
+ }
304
+ }
305
+
306
+ public getUIConfigSections(): UIConfigSections {
307
+ this.ensureInitialized();
308
+ return this.uiConfigSections;
309
+ }
310
+
311
+ public getCurrentConfig(): Config {
312
+ this.ensureInitialized();
313
+ return JSON.parse(JSON.stringify(this.currentConfig));
314
+ }
315
+ }
316
+
317
+ const configManager = new ConfigManager();
318
+
319
+ export default configManager;