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
1 change: 0 additions & 1 deletion python/configs/agent_cards/sec_agent.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"name": "SecAgent",
"display_name": "SEC Agent",
"url": "http://localhost:10003/",
"icon_url": "https://valuecell-test.oss-cn-hangzhou.aliyuncs.com/images/sec_agent.png",
"description": "SecAgent can analyze SEC filings like 10-Q, 10-K, 13-F and analyze stock holdings of institutional investment managers. It can chat about stock performance, financial metrics, and market trends or track specific stocks and provide updates.",
"skills": [
{
Expand Down
30 changes: 25 additions & 5 deletions python/valuecell/agents/sec_agent.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import asyncio
import hashlib
import json
import logging
import os
from datetime import datetime
Expand All @@ -17,6 +18,7 @@
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
SEC_FILLINGS_COMPONENT_TYPE = "sec_feed"


class QueryType(str, Enum):
Expand Down Expand Up @@ -201,7 +203,7 @@ async def _detect_filing_changes(
async def _generate_filing_summary(
self, ticker: str, changed_filings: Dict[str, bool]
) -> str:
"""Generate AI summary of filing changes"""
"""Generate AI summary of filing changes in JSON format"""
try:
changed_types = [
filing_type
Expand All @@ -210,7 +212,7 @@ async def _generate_filing_summary(
]

if not changed_types:
return f"No new filings detected for {ticker}."
return ""

summary_prompt = f"""
New SEC filings have been detected for {ticker}. The following filing types have been updated:
Expand All @@ -228,11 +230,26 @@ async def _generate_filing_summary(
"""

response = await self.analysis_agent.arun(summary_prompt)
return response.content

# Create JSON structure
summary_data = {
"ticker": ticker,
"source": "SEC",
"data": response.content,
"create_time": datetime.now().isoformat(),
}

return json.dumps(summary_data)

except Exception as e:
logger.error(f"Failed to generate filing summary: {e}")
return f"New filings detected for {ticker}: {', '.join(changed_types)}, but summary generation failed."
error_summary_data = {
"ticker": ticker,
"source": "SEC",
"data": f"New filings detected for {ticker}: {', '.join(changed_types) if 'changed_types' in locals() else 'unknown'}, but summary generation failed.",
"create_time": datetime.now().isoformat(),
}
return ""

async def _classify_query(self, query: str) -> QueryType:
"""
Expand Down Expand Up @@ -569,7 +586,10 @@ async def notify(self, query: str, session_id: str, task_id: str):
ticker, changes
)

yield notification.component_generator(summary, "sec_feed")
if summary:
yield notification.component_generator(
summary, SEC_FILLINGS_COMPONENT_TYPE
)

# Wait before next check
await asyncio.sleep(check_interval)
Expand Down
183 changes: 183 additions & 0 deletions start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
#!/usr/bin/env bash
set -Eeuo pipefail

# Simple project launcher with auto-install for bun and uv
# - macOS: use Homebrew to install missing tools
# - other OS: print guidance

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}" )" && pwd)"
FRONTEND_DIR="$SCRIPT_DIR/frontend"
PY_DIR="$SCRIPT_DIR/python"

BACKEND_PID=""
FRONTEND_PID=""

info() { echo "[INFO] $*"; }
success(){ echo "[ OK ] $*"; }
warn() { echo "[WARN] $*"; }
error() { echo "[ERR ] $*" 1>&2; }

command_exists() { command -v "$1" >/dev/null 2>&1; }

ensure_brew_on_macos() {
if [[ "${OSTYPE:-}" == darwin* ]]; then
if ! command_exists brew; then
error "Homebrew is not installed. Please install Homebrew: https://brew.sh/"
error "Example install: /bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\""
exit 1
fi
fi
}

ensure_tool() {
local tool_name="$1"; shift
local brew_formula="$1"; shift || true

if command_exists "$tool_name"; then
success "$tool_name is installed ($($tool_name --version 2>/dev/null | head -n1 || echo version unknown))"
return 0
fi

case "$(uname -s)" in
Darwin)
ensure_brew_on_macos
info "Installing $tool_name via Homebrew..."
brew install "$brew_formula"
;;
Linux)
info "Detected Linux, auto-installing $tool_name..."
if [[ "$tool_name" == "bun" ]]; then
curl -fsSL https://bun.sh/install | bash
# Add Bun default install dir to PATH (current process only)
if ! command_exists bun && [[ -x "$HOME/.bun/bin/bun" ]]; then
export PATH="$HOME/.bun/bin:$PATH"
fi
elif [[ "$tool_name" == "uv" ]]; then
curl -LsSf https://astral.sh/uv/install.sh | sh
# Add uv default install dir to PATH (current process only)
if ! command_exists uv && [[ -x "$HOME/.local/bin/uv" ]]; then
export PATH="$HOME/.local/bin:$PATH"
fi
else
warn "Unknown tool: $tool_name"
fi
;;
*)
warn "$tool_name not installed. Auto-install is not provided on this OS. Please install manually and retry."
exit 1
;;
esac

if command_exists "$tool_name"; then
success "$tool_name installed successfully"
else
error "$tool_name installation failed. Please install manually and retry."
exit 1
fi
}

install_dependencies() {
# Backend deps
if [[ -d "$PY_DIR" ]]; then
info "Sync Python dependencies (uv sync)..."
(cd "$PY_DIR" && uv sync)
success "Python dependencies synced"
else
warn "Backend directory not found: $PY_DIR. Skipping"
fi

# Frontend deps
if [[ -d "$FRONTEND_DIR" ]]; then
info "Install frontend dependencies (bun install)..."
(cd "$FRONTEND_DIR" && bun install)
success "Frontend dependencies installed"
} else {
warn "Frontend directory not found: $FRONTEND_DIR. Skipping"
fi
}

start_backend() {
if [[ ! -d "$PY_DIR" ]]; then
warn "Backend directory not found; skipping backend start"
return 0
fi
info "Starting backend (uv run python -m valuecell.server.main)..."
(
cd "$PY_DIR" && uv run python -m valuecell.server.main
) & BACKEND_PID=$!
info "Backend PID: $BACKEND_PID"
}

start_frontend() {
if [[ ! -d "$FRONTEND_DIR" ]]; then
warn "Frontend directory not found; skipping frontend start"
return 0
fi
info "Starting frontend dev server (bun run dev)..."
(
cd "$FRONTEND_DIR" && bun run dev
) & FRONTEND_PID=$!
info "Frontend PID: $FRONTEND_PID"
}

cleanup() {
echo
info "Stopping services..."
if [[ -n "$FRONTEND_PID" ]] && kill -0 "$FRONTEND_PID" 2>/dev/null; then
kill "$FRONTEND_PID" 2>/dev/null || true
fi
if [[ -n "$BACKEND_PID" ]] && kill -0 "$BACKEND_PID" 2>/dev/null; then
kill "$BACKEND_PID" 2>/dev/null || true
fi
success "Stopped"
}

trap cleanup EXIT INT TERM

print_usage() {
cat <<'EOF'
Usage: ./start.sh [options]

Description:
- Checks whether bun and uv are installed; on macOS, missing tools will be auto-installed via Homebrew.
- Then installs backend and frontend dependencies and starts services.

Options:
--no-frontend Start backend only
--no-backend Start frontend only
-h, --help Show help
EOF
}

main() {
local start_frontend_flag=1
local start_backend_flag=1

while [[ $# -gt 0 ]]; do
case "$1" in
--no-frontend) start_frontend_flag=0; shift ;;
--no-backend) start_backend_flag=0; shift ;;
-h|--help) print_usage; exit 0 ;;
*) error "Unknown argument: $1"; print_usage; exit 1 ;;
esac
done

# Ensure tools
ensure_tool bun oven-sh/bun/bun
ensure_tool uv uv

install_dependencies

if (( start_backend_flag )); then
start_backend
fi
if (( start_frontend_flag )); then
start_frontend
fi

info "Services started. Press Ctrl+C to stop."
# Wait for background jobs
wait
}

main "$@"