Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 57 additions & 45 deletions python/configs/providers/openrouter.yaml
Original file line number Diff line number Diff line change
@@ -1,50 +1,62 @@
# ============================================
# OpenRouter Provider Configuration
# ============================================
name: "OpenRouter"
provider_type: "openrouter"

enabled: true # Default is true if not specified

# Connection Configuration
connection:
base_url: "https://openrouter.ai/api/v1"
api_key_env: "OPENROUTER_API_KEY"

# Default model if none specified
default_model: "anthropic/claude-haiku-4.5"

# Model Parameters Defaults
# ============================================
# OpenRouter Provider Configuration
# ============================================
name: "OpenRouter"
provider_type: "openrouter"

enabled: true # Default is true if not specified

# Connection Configuration
connection:
base_url: "https://openrouter.ai/api/v1"
api_key_env: "OPENROUTER_API_KEY"

# Default model if none specified
default_model: "anthropic/claude-haiku-4.5"

# Model Parameters Defaults
defaults:
temperature: 0.5

# Extra headers for OpenRouter API
extra_headers:
HTTP-Referer: "https://valuecell.ai"
X-Title: "ValueCell"

# Available Models (commonly used)
models:
- id: "anthropic/claude-haiku-4.5"
name: "Claude Haiku 4.5"
- id: "x-ai/grok-4"
name: "Grok 4"
- id: "qwen/qwen-max"
name: "Qwen Max"
- id: "openai/gpt-5"
name: "GPT-5"
- id: "google/gemini-2.5-flash"
name: "Gemini 2.5 Flash"
- id: "google/gemini-2.5-pro"
name: "Gemini 2.5 Pro"

# ============================================
# Embedding Models Configuration
# ============================================
# OpenRouter provides embedding models
embedding:
# Default embedding model
default_model: "qwen/qwen3-embedding-4b"

# Default parameters
defaults:
temperature: 0.5

# Extra headers for OpenRouter API
extra_headers:
HTTP-Referer: "https://valuecell.ai"
X-Title: "ValueCell"
dimensions: 2560
encoding_format: "float"

# Available Models (commonly used)
# Available embedding models
models:

- id: "anthropic/claude-haiku-4.5"
name: "Claude Haiku 4.5"

- id: "x-ai/grok-4"
name: "Grok 4"

- id: "qwen/qwen-max"
name: "Qwen Max"

- id: "openai/gpt-5"
name: "GPT-5"

- id: "google/gemini-2.5-flash"
name: "Gemini 2.5 Flash"

- id: "google/gemini-2.5-pro"
name: "Gemini 2.5 Pro"

# Note: OpenRouter does not support embedding models
# For embedding, use other providers like OpenAI or SiliconFlow etc
- id: "qwen/qwen3-embedding-4b"
name: "Qwen3 Embedding 4B"
dimensions: 2560
max_input: 32768
description: "Qwen3 Embedding Model"


15 changes: 12 additions & 3 deletions python/valuecell/agents/research_agent/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from loguru import logger

import valuecell.utils.model as model_utils_mod
from valuecell.agents.research_agent.knowledge import knowledge
from valuecell.agents.research_agent.knowledge import get_knowledge
from valuecell.agents.research_agent.prompts import (
KNOWLEDGE_AGENT_EXPECTED_OUTPUT,
KNOWLEDGE_AGENT_INSTRUCTION,
Expand Down Expand Up @@ -37,6 +37,8 @@ def __init__(self, **kwargs):
# search_crypto_vcs,
# search_crypto_people,
]
# Lazily obtain knowledge; disable search if unavailable
knowledge = get_knowledge()
self.knowledge_research_agent = Agent(
model=model_utils_mod.get_model_for_agent("research_agent"),
instructions=[KNOWLEDGE_AGENT_INSTRUCTION],
Expand All @@ -45,7 +47,7 @@ def __init__(self, **kwargs):
knowledge=knowledge,
db=InMemoryDb(),
# context
search_knowledge=True,
search_knowledge=knowledge is not None,
add_datetime_to_context=True,
add_history_to_context=True,
num_history_runs=3,
Expand All @@ -54,7 +56,14 @@ def __init__(self, **kwargs):
# configuration
debug_mode=agent_debug_mode_enabled(),
)
set_identity(os.getenv("SEC_EMAIL"))
# Configure EDGAR identity only when SEC_EMAIL is present
sec_email = os.getenv("SEC_EMAIL")
if sec_email:
set_identity(sec_email)
else:
logger.warning(
"SEC_EMAIL not set; EDGAR identity is not configured for ResearchAgent."
)

async def stream(
self,
Expand Down
53 changes: 48 additions & 5 deletions python/valuecell/agents/research_agent/knowledge.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,57 @@
from agno.knowledge.knowledge import Knowledge
from agno.knowledge.reader.markdown_reader import MarkdownReader
from agno.knowledge.reader.pdf_reader import PDFReader
from loguru import logger

from .vdb import get_vector_db

_knowledge_cache: Optional[Knowledge] = None


def get_knowledge() -> Optional[Knowledge]:
"""Lazily create and cache the Knowledge instance.

Returns None when embeddings/vector DB are unavailable, enabling a
tools-only mode without knowledge search.
"""
global _knowledge_cache
if _knowledge_cache is not None:
return _knowledge_cache

vdb = get_vector_db()
if vdb is None:
logger.warning(
"ResearchAgent Knowledge disabled: vector DB unavailable (no embeddings)."
)
return None

try:
_knowledge_cache = Knowledge(
vector_db=vdb,
max_results=10,
)
return _knowledge_cache
except Exception as e:
logger.warning(
"Failed to create Knowledge for ResearchAgent; disabling. Error: {}",
e,
)
return None

from .vdb import vector_db

knowledge = Knowledge(
vector_db=vector_db,
max_results=10,
)
md_reader = MarkdownReader(chunking_strategy=MarkdownChunking())
pdf_reader = PDFReader(chunking_strategy=MarkdownChunking())


async def insert_md_file_to_knowledge(
name: str, path: Path, metadata: Optional[dict] = None
):
knowledge = get_knowledge()
if knowledge is None:
logger.warning(
"Skipping markdown insertion: Knowledge disabled (no embeddings configured)."
)
return
await knowledge.add_content_async(
name=name,
path=path,
Expand All @@ -28,6 +65,12 @@ async def insert_md_file_to_knowledge(


async def insert_pdf_file_to_knowledge(url: str, metadata: Optional[dict] = None):
knowledge = get_knowledge()
if knowledge is None:
logger.warning(
"Skipping PDF insertion: Knowledge disabled (no embeddings configured)."
)
return
await knowledge.add_content_async(
url=url,
metadata=metadata,
Expand Down
Loading