Commit graph

100 commits

Author SHA1 Message Date
31047e6fd2 ci: include meet_kevin extras in test step (yt-dlp, feedparser, anthropic, httpx) 2026-05-22 20:00:59 +00:00
8a1d03a967 refactor(meet-kevin): switch LLM back to native Anthropic SDK with OAuth bearer
Previous refactor (89f01ad) moved to OpenRouter because no sk-ant-api-* key
was found in Vault. Turns out claude-agent-service-spare-{1,2} hold
sk-ant-oat01-* OAuth tokens (108 chars, scope user:inference, 1-year TTL,
minted via 'claude setup-token' — see memory id=832).

These tokens work with the Anthropic SDK via the auth_token= constructor
argument (routes to Authorization: Bearer ... instead of x-api-key: ...).
They consume the Enterprise Claude subscription quota rather than
per-call billing, so the OpenRouter zero-credit problem goes away.

- llm_analyzer.py: revert OpenAI client to AsyncAnthropic; tool-use API
  + cache_control restored
- config.py: openrouter_api_key -> anthropic_oauth_token; model slug
  reverted from anthropic/claude-sonnet-4.5 -> claude-sonnet-4-5
- main.py: AsyncOpenAI -> AsyncAnthropic(auth_token=...), drop OpenRouter
  attribution headers
- pyproject: openai>=1.50 -> anthropic>=0.40 in meet_kevin extras
- tests: mocks ported back to messages.create + tool_use blocks
2026-05-22 19:24:40 +00:00
4f4d365652 fix(docker): add ffmpeg + nodejs for yt-dlp YouTube extraction
yt-dlp requires:
- ffmpeg for sub-conversion (--convert-subs srt) and any format mux
- A JS runtime for YouTube player decryption (deno or node)

Without these, every caption extraction attempt in the meet-kevin-watcher
container fails with 'ffmpeg not found' + 'No supported JavaScript runtime
could be found'. Adding both to the runtime stage of Dockerfile.service.

Size impact: ~50 MB.
2026-05-22 14:03:22 +00:00
89f01ad9c0 refactor(meet-kevin): switch LLM analyzer to OpenRouter via OpenAI SDK
User's Vault has openrouter_api_key but no direct sk-ant-* Anthropic key.
OpenRouter passes through Claude Sonnet 4.6 (~3% markup over Anthropic
list pricing) and matches the existing gpt_mini_endpoint pattern used
by recruiter-responder.

- Replace anthropic.AsyncAnthropic with openai.AsyncOpenAI + base_url
- Convert Anthropic tool-use API to OpenAI function-calling
- System prompt unchanged (analyst instructions are model-agnostic)
- Drop cache_control (not in OpenAI API); revisit later if cost matters
- Model slug: anthropic/claude-sonnet-4.5 (OpenRouter's current Claude tier)
- Pricing: $3.10/M input, $15.50/M output (OpenRouter pass-through)
- Config field anthropic_api_key -> openrouter_api_key
- pyproject extras: anthropic>=0.40 -> openai>=1.50

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 09:52:55 +00:00
3c20c8c12c feat: add meet_kevin extras (yt-dlp, feedparser, anthropic) 2026-05-21 20:16:09 +00:00
7b81980c66 fix(meet-kevin): API smoke-test bugs from Task 17 QA
Three issues caught during end-to-end manual QA against docker-compose:

1. SAEnum field columns serialized to Python enum NAMES ('DISCOVERED')
   but the DB enum had VALUES ('discovered'). Added `values_callable`
   to all 5 SAEnum() declarations in shared/models/meet_kevin.py so they
   emit values, matching the migration's enum literals.

2. /dashboard's "last 7 days" / "last 14 days" filters used
   `func.cast("7 days", type_=None)` which produced NullType DDL.
   Replaced with `text("now() - interval '7 days'")`.

3. /dashboard's outlook trend query repeated `func.date_trunc("day", col)`
   in SELECT, GROUP BY and ORDER BY — Postgres treats each as a separate
   parameterized expression. Hoisted into a single `day_trunc` variable
   so all three clauses reference the same SQL fragment.

All 11 /api/meet-kevin/* endpoints now return valid JSON against a
docker-compose Postgres seeded with one analyzed video + NVDA mention.
2026-05-21 20:15:08 +00:00
01856bab9f feat(dashboard): wire Meet Kevin routes + sidebar entry 2026-05-21 20:08:08 +00:00
6bcb6637a8 feat(dashboard): Meet Kevin stocks list + per-ticker drill-down 2026-05-21 20:07:01 +00:00
9ce0e44929 feat(dashboard): Meet Kevin video detail page (tabs + iframe + deep-links) 2026-05-21 20:05:14 +00:00
625c22b833 feat(dashboard): Meet Kevin videos feed page 2026-05-21 20:03:23 +00:00
d4a1ca870e feat(dashboard): Meet Kevin home page 2026-05-21 20:01:53 +00:00
a4d75e37c4 feat(dashboard): reusable Meet Kevin components (ActionChip, ConvictionBar, YouTubeEmbed) 2026-05-21 19:58:36 +00:00
83b18b43cf revert: keep original dashboard tsconfig (Task 10 didn't need to change it) 2026-05-21 19:57:30 +00:00
cafcaad502 feat(dashboard): Meet Kevin TypeScript types + API client 2026-05-21 19:56:13 +00:00
bfa7a503da feat(api): /api/meet-kevin/* routes (11 endpoints) 2026-05-21 19:53:16 +00:00
8f5ee8f1c3 feat(meet-kevin): pipeline orchestrator + service main loop
Implements Task 8 of the Meet Kevin revival plan.

- pipeline.py: PipelineDeps dataclass (frozen, DI-friendly), process_one_video
  state machine (discovered→captioned→analyzed with retry/cost-cap logic),
  and daily_cost_used() SQL helper.
- main.py: async run() entry point with RSS poll loop, per-video pipeline
  processing, OTEL counters, SIGTERM/SIGINT shutdown, httpx client lifecycle,
  and clean Anthropic/DB teardown.
- tests: 5 pipeline unit tests (happy path, no captions, cost cap, retry
  increment, failed-after-3-retries) all passing; full watcher suite 56/56.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 19:48:43 +00:00
8309556c00 feat(meet-kevin): Claude Sonnet 4.6 LLM analyzer (tool-use forcing + prompt cache)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 19:44:57 +00:00
145f7dbec5 feat(meet-kevin): caption extractor via yt-dlp
- Implement CaptionResult frozen dataclass for structured caption data
- Add parse_srt() to parse SubRip format with flexible timestamp handling
- Add extract_captions() async function using yt-dlp subprocess wrapper
- Prefer manual captions over auto-generated; clean up SRT files after parsing
- Add 16 comprehensive tests covering edge cases (empty input, malformed SRT,
  timestamp variations, language extraction, manual vs auto selection)
- Type-safe implementation with full mypy --strict compliance
- Add sample.srt fixture with 3 segments mentioning NVDA for test reference
2026-05-21 19:40:52 +00:00
8ce3ede09c feat(meet-kevin): RSS poller for YouTube uploads 2026-05-21 19:36:22 +00:00
8edcb070ed feat: scaffold meet_kevin_watcher service + config 2026-05-21 19:33:04 +00:00
61adf63c7d feat: add Alembic migration for Meet Kevin tables 2026-05-21 19:31:21 +00:00
a49e46f787 fix(models): drop dead __table_args__ + use func.now() for server defaults 2026-05-21 19:26:42 +00:00
8ed2e70e8f feat: add Meet Kevin SQLAlchemy models (5 tables) 2026-05-21 19:19:33 +00:00
8a412e6ae9 fix(schemas): use enum types as field types + enforce symbol length
- Replace all Literal[...] type annotations with corresponding enum classes
  (TickerAction, TimeHorizon, MarketOutlook, VideoStatus, TranscriptSource)
  for MeetKevinTickerMention, MeetKevinAnalysis, and API response models
  (VideoSummary, VideoDetail, StockMention, StockSummary, TimelineBucket)
- Add min_length=1, max_length=10 validation to MeetKevinTickerMention.symbol
- Split test_conviction_edge_cases into two separate boundary tests
- Strengthen test_valid_ticker_mention with assertions for all 6 fields
- Trim no-information docstrings from TranscriptSegment, StockTimeline
- All 60 schema tests pass
2026-05-21 19:15:59 +00:00
75534de71b feat: add Meet Kevin pydantic schemas (analysis + API shapes) 2026-05-21 19:06:04 +00:00
8f616e6487 add Meet Kevin revival implementation plan
22-task plan across 6 phases (data layer, watcher service stages,
API gateway, dashboard, container/deps, K8s revival). Each task
includes TDD checkboxes, file paths, acceptance criteria, and
commit commands. References the design doc (ab382af) for schemas,
prompts, and the Terraform diff.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 19:01:24 +00:00
ab382af3f5 add Meet Kevin revival design document
Design for reviving the trading-bot K8s stack with a new Meet Kevin
YouTube watcher pipeline. v1 scope: poll RSS every 3h, extract
captions via yt-dlp, run transcript through Claude Sonnet 4.6 for
structured per-ticker recommendations and market outlook, surface
in a new ticker-centric UI under /meet-kevin/*. Bot integration
(signal_generator) and auto-trading deferred to v2.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 18:42:46 +00:00
Viktor Barzin
072ea015fd
ci: retrigger build 2026-03-15 22:53:07 +00:00
Viktor Barzin
59d2e634c8
[ci] rebuild 2026-02-28 23:00:48 +00:00
Viktor Barzin
02d6f717a5
[ci] rebuild after agent restart
Some checks are pending
ci/woodpecker/push/woodpecker Pipeline is running
2026-02-28 15:10:08 +00:00
Viktor Barzin
b64b15e737
[ci] rebuild
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-02-28 14:47:20 +00:00
Viktor Barzin
d5d27adf91
[ci] rebuild
Some checks are pending
ci/woodpecker/push/woodpecker Pipeline is running
2026-02-28 11:37:30 +00:00
Viktor Barzin
121ece5702
fix: add __main__ entrypoint to api-gateway so it starts under python -m
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Without this block, `python -m services.api_gateway.main` just imports
the module and exits with code 0, causing CrashLoopBackOff.
2026-02-28 11:22:52 +00:00
Viktor Barzin
e92cbc1bc4
fix: trade log Invalid Date and equity curve duplicate timestamp crash
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
- TradeLog: use created_at (from API) instead of timestamp for date display
- EquityCurve: deduplicate data by day before passing to lightweight-charts,
  preventing "data must be asc ordered by time" assertion failure when
  multiple snapshots exist on the same day

Made-with: Cursor
2026-02-28 11:05:37 +00:00
Viktor Barzin
4f60ef453f
fix: hardcode nginx-k8s.conf in dashboard Dockerfile
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
The buildx plugin was not passing NGINX_CONF build arg correctly,
causing the docker-compose nginx config (with hostname api-gateway)
to be used instead of the K8s one (with localhost).
2026-02-25 23:46:47 +00:00
Viktor Barzin
0a017f52cb
perf: switch to uv for faster dependency installation
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
- Replace pip with uv in Dockerfile.service builder stage (~5-10x faster)
- Replace pip with uv in CI test step
- Separate pyproject.toml copy from source copy in Dockerfile for better
  Docker layer caching (deps only reinstalled when pyproject.toml changes)
- Add cache_from for buildx to reuse layers from previous builds
- Remove pip cache workaround from test step (not persisted in K8s)
2026-02-25 22:55:58 +00:00
Viktor Barzin
5955a5a86d
fix: hardcode pip extras in Dockerfile to avoid buildx arg parsing issues
Some checks are pending
ci/woodpecker/push/woodpecker Pipeline is running
The woodpeckerci/plugin-docker-buildx was not passing the EXTRAS build
arg correctly (commas in the value were likely being parsed as list
separators), causing the image to only install dev dependencies instead
of all service extras (api, news, sentiment, trading, backtester).

Hardcode the pip install extras directly in the Dockerfile rather than
relying on the build arg.
2026-02-25 22:27:15 +00:00
Viktor Barzin
a3cdd0f1a5
fix: resolve all remaining TODOs, add dev mode auth bypass
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
- Learning engine: expand default weights from 3 to all 9 strategies
- Learning engine: resolve placeholder strategy_id with DB lookup
- Learning engine: pass strategy_sources from trade execution
- Trade executor: respect trading:paused Redis flag in RiskManager
- Portfolio sync: compute actual daily P&L from day-start snapshot
- Portfolio API: cumulative P&L from first snapshot, read pause flag
- Portfolio metrics: compute max drawdown and avg hold duration
- Add strategy_sources field to TradeExecution schema
- Add dev_mode config (TRADING_DEV_MODE) to bypass auth for local dev
- Dashboard: VITE_DEV_MODE bypasses ProtectedRoute and 401 redirects
- Vite proxy target configurable via VITE_API_TARGET
- Add top-level README.md and remaining-work-plan.md
- Update CLAUDE.md with correct counts and remove stale TODOs
- 404 tests passing

Made-with: Cursor
2026-02-25 22:02:25 +00:00
Viktor Barzin
4094e4b10f
fix: push final tags directly from buildx, remove publish-images step
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
The publish-images step used an alpine container to run skopeo for
re-tagging, but intermittent DNS failures prevent apk from installing
packages. Instead, have the buildx plugin push with both the pipeline
number tag and latest tag directly, eliminating the extra step.
2026-02-25 21:25:27 +00:00
Viktor Barzin
e73d62cd3a
fix: make hypertable creation conditional on TimescaleDB extension
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
The alembic migration unconditionally called create_hypertable() which
fails if TimescaleDB isn't installed on the PostgreSQL instance. Wrap
in a DO block that checks for the extension first.
2026-02-25 21:03:31 +00:00
Viktor Barzin
efc91e9ad0
[ci] test build
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-02-25 20:51:19 +00:00
Viktor Barzin
1dd0c25cbc
fix: escape shell variables from Woodpecker CI substitution
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Woodpecker pre-processes ${VAR} syntax as CI variables, replacing
undefined ones with empty strings. Use $$ escaping for shell variables
to prevent Woodpecker from consuming them. The ${REPO} variable in
the skopeo publish step was being replaced with empty string.
2026-02-25 00:43:01 +00:00
Viktor Barzin
792776bfe0
fix: use woodpeckerci/plugin-docker-buildx for privileged builds
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
plugins/docker does not get privileged: true in K8s pods despite being
in WOODPECKER_PLUGINS_PRIVILEGED. woodpeckerci/plugin-docker-buildx
correctly receives privileged mode. Previous build failure with buildx
was a transient network timeout reaching registry-1.docker.io.
2026-02-25 00:25:56 +00:00
Viktor Barzin
d084dad021
[ci] test privileged mode
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-02-24 23:59:06 +00:00
Viktor Barzin
61f1efbf27
fix: switch back to plugins/docker for image builds
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
The woodpeckerci/plugin-docker-buildx plugin started dockerd correctly
(privileged mode works) but failed DockerHub authentication. Switch back
to plugins/docker which is proven working in realestate-crawler pipeline.
2026-02-24 23:42:53 +00:00
Viktor Barzin
1f065f4b4d
fix: switch to buildx plugin and add pip caching for CI
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
- Use woodpeckerci/plugin-docker-buildx instead of plugins/docker to fix
  Docker daemon connection failures (privileged mode not applied)
- Add pip dependency caching between builds via /woodpecker/pip-cache
- Fix slack plugin image (plugins/slack, not woodpeckerci/plugin-slack)
- Use proper buildx cache_from syntax (type=registry,ref=...)
2026-02-24 23:27:33 +00:00
Viktor Barzin
3bbb82b6aa
[ci] trigger build
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2026-02-24 23:12:45 +00:00
Viktor Barzin
4df7c67c13
fix: news articles showing 1970 dates when published_at is null
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Backend falls back to fetched_at when published_at is NULL in the database,
so the API always returns a meaningful date. Frontend also handles null
defensively to avoid new Date(null) producing Unix epoch 0.
2026-02-23 22:48:16 +00:00
Viktor Barzin
9f86aaf540
add K8s nginx config and parameterize Dockerfile.dashboard
Create nginx-k8s.conf that proxies to localhost:8000 instead of
api-gateway:8000 for K8s pods where both containers share a network
namespace. Update Dockerfile.dashboard to accept a NGINX_CONF build arg
(defaults to docker/nginx.conf for docker-compose compatibility).
2026-02-23 22:27:52 +00:00
Viktor Barzin
ed3bf57566
add Woodpecker CI pipeline 2026-02-23 22:26:49 +00:00